1 use crate::{
2 fluent_generated as fluent,
3 lints::{
4 AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes,
5 InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion,
6 OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub,
7 OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral,
8 OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange,
9 VariantSizeDifferencesDiag,
10 },
11 };
12 use crate::{LateContext, LateLintPass, LintContext};
13 use rustc_ast as ast;
14 use rustc_attr as attr;
15 use rustc_data_structures::fx::FxHashSet;
16 use rustc_errors::DiagnosticMessage;
17 use rustc_hir as hir;
18 use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
19 use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
20 use rustc_middle::ty::subst::SubstsRef;
21 use rustc_middle::ty::{
22 self, AdtKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
23 };
24 use rustc_span::def_id::LocalDefId;
25 use rustc_span::source_map;
26 use rustc_span::symbol::sym;
27 use rustc_span::{Span, Symbol};
28 use rustc_target::abi::{Abi, Size, WrappingRange};
29 use rustc_target::abi::{Integer, TagEncoding, Variants};
30 use rustc_target::spec::abi::Abi as SpecAbi;
31
32 use std::iter;
33 use std::ops::ControlFlow;
34
35 declare_lint! {
36 /// The `unused_comparisons` lint detects comparisons made useless by
37 /// limits of the types involved.
38 ///
39 /// ### Example
40 ///
41 /// ```rust
42 /// fn foo(x: u8) {
43 /// x >= 0;
44 /// }
45 /// ```
46 ///
47 /// {{produces}}
48 ///
49 /// ### Explanation
50 ///
51 /// A useless comparison may indicate a mistake, and should be fixed or
52 /// removed.
53 UNUSED_COMPARISONS,
54 Warn,
55 "comparisons made useless by limits of the types involved"
56 }
57
58 declare_lint! {
59 /// The `overflowing_literals` lint detects literal out of range for its
60 /// type.
61 ///
62 /// ### Example
63 ///
64 /// ```rust,compile_fail
65 /// let x: u8 = 1000;
66 /// ```
67 ///
68 /// {{produces}}
69 ///
70 /// ### Explanation
71 ///
72 /// It is usually a mistake to use a literal that overflows the type where
73 /// it is used. Either use a literal that is within range, or change the
74 /// type to be within the range of the literal.
75 OVERFLOWING_LITERALS,
76 Deny,
77 "literal out of range for its type"
78 }
79
80 declare_lint! {
81 /// The `variant_size_differences` lint detects enums with widely varying
82 /// variant sizes.
83 ///
84 /// ### Example
85 ///
86 /// ```rust,compile_fail
87 /// #![deny(variant_size_differences)]
88 /// enum En {
89 /// V0(u8),
90 /// VBig([u8; 1024]),
91 /// }
92 /// ```
93 ///
94 /// {{produces}}
95 ///
96 /// ### Explanation
97 ///
98 /// It can be a mistake to add a variant to an enum that is much larger
99 /// than the other variants, bloating the overall size required for all
100 /// variants. This can impact performance and memory usage. This is
101 /// triggered if one variant is more than 3 times larger than the
102 /// second-largest variant.
103 ///
104 /// Consider placing the large variant's contents on the heap (for example
105 /// via [`Box`]) to keep the overall size of the enum itself down.
106 ///
107 /// This lint is "allow" by default because it can be noisy, and may not be
108 /// an actual problem. Decisions about this should be guided with
109 /// profiling and benchmarking.
110 ///
111 /// [`Box`]: https://doc.rust-lang.org/std/boxed/index.html
112 VARIANT_SIZE_DIFFERENCES,
113 Allow,
114 "detects enums with widely varying variant sizes"
115 }
116
117 declare_lint! {
118 /// The `invalid_nan_comparisons` lint checks comparison with `f32::NAN` or `f64::NAN`
119 /// as one of the operand.
120 ///
121 /// ### Example
122 ///
123 /// ```rust
124 /// let a = 2.3f32;
125 /// if a == f32::NAN {}
126 /// ```
127 ///
128 /// {{produces}}
129 ///
130 /// ### Explanation
131 ///
132 /// NaN does not compare meaningfully to anything – not
133 /// even itself – so those comparisons are always false.
134 INVALID_NAN_COMPARISONS,
135 Warn,
136 "detects invalid floating point NaN comparisons"
137 }
138
139 #[derive(Copy, Clone)]
140 pub struct TypeLimits {
141 /// Id of the last visited negated expression
142 negated_expr_id: Option<hir::HirId>,
143 }
144
145 impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS, INVALID_NAN_COMPARISONS]);
146
147 impl TypeLimits {
new() -> TypeLimits148 pub fn new() -> TypeLimits {
149 TypeLimits { negated_expr_id: None }
150 }
151 }
152
153 /// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint (`expr..MAX+1`).
154 /// Returns `true` iff the lint was emitted.
lint_overflowing_range_endpoint<'tcx>( cx: &LateContext<'tcx>, lit: &hir::Lit, lit_val: u128, max: u128, expr: &'tcx hir::Expr<'tcx>, ty: &str, ) -> bool155 fn lint_overflowing_range_endpoint<'tcx>(
156 cx: &LateContext<'tcx>,
157 lit: &hir::Lit,
158 lit_val: u128,
159 max: u128,
160 expr: &'tcx hir::Expr<'tcx>,
161 ty: &str,
162 ) -> bool {
163 // Look past casts to support cases like `0..256 as u8`
164 let (expr, lit_span) = if let Node::Expr(par_expr) = cx.tcx.hir().get(cx.tcx.hir().parent_id(expr.hir_id))
165 && let ExprKind::Cast(_, _) = par_expr.kind {
166 (par_expr, expr.span)
167 } else {
168 (expr, expr.span)
169 };
170
171 // We only want to handle exclusive (`..`) ranges,
172 // which are represented as `ExprKind::Struct`.
173 let par_id = cx.tcx.hir().parent_id(expr.hir_id);
174 let Node::ExprField(field) = cx.tcx.hir().get(par_id) else { return false };
175 let Node::Expr(struct_expr) = cx.tcx.hir().get_parent(field.hir_id) else { return false };
176 if !is_range_literal(struct_expr) {
177 return false;
178 };
179 let ExprKind::Struct(_, eps, _) = &struct_expr.kind else { return false };
180 if eps.len() != 2 {
181 return false;
182 }
183
184 // We can suggest using an inclusive range
185 // (`..=`) instead only if it is the `end` that is
186 // overflowing and only by 1.
187 if !(eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max) {
188 return false;
189 };
190
191 use rustc_ast::{LitIntType, LitKind};
192 let suffix = match lit.node {
193 LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
194 LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
195 LitKind::Int(_, LitIntType::Unsuffixed) => "",
196 _ => bug!(),
197 };
198
199 let sub_sugg = if expr.span.lo() == lit_span.lo() {
200 let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) else { return false };
201 UseInclusiveRange::WithoutParen {
202 sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()),
203 start,
204 literal: lit_val - 1,
205 suffix,
206 }
207 } else {
208 UseInclusiveRange::WithParen {
209 eq_sugg: expr.span.shrink_to_lo(),
210 lit_sugg: lit_span,
211 literal: lit_val - 1,
212 suffix,
213 }
214 };
215
216 cx.emit_spanned_lint(
217 OVERFLOWING_LITERALS,
218 struct_expr.span,
219 RangeEndpointOutOfRange { ty, sub: sub_sugg },
220 );
221
222 // We've just emitted a lint, special cased for `(...)..MAX+1` ranges,
223 // return `true` so the callers don't also emit a lint
224 true
225 }
226
227 // For `isize` & `usize`, be conservative with the warnings, so that the
228 // warnings are consistent between 32- and 64-bit platforms.
int_ty_range(int_ty: ty::IntTy) -> (i128, i128)229 fn int_ty_range(int_ty: ty::IntTy) -> (i128, i128) {
230 match int_ty {
231 ty::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()),
232 ty::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()),
233 ty::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()),
234 ty::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()),
235 ty::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()),
236 ty::IntTy::I128 => (i128::MIN, i128::MAX),
237 }
238 }
239
uint_ty_range(uint_ty: ty::UintTy) -> (u128, u128)240 fn uint_ty_range(uint_ty: ty::UintTy) -> (u128, u128) {
241 let max = match uint_ty {
242 ty::UintTy::Usize => u64::MAX.into(),
243 ty::UintTy::U8 => u8::MAX.into(),
244 ty::UintTy::U16 => u16::MAX.into(),
245 ty::UintTy::U32 => u32::MAX.into(),
246 ty::UintTy::U64 => u64::MAX.into(),
247 ty::UintTy::U128 => u128::MAX,
248 };
249 (0, max)
250 }
251
get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option<String>252 fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option<String> {
253 let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?;
254 let firstch = src.chars().next()?;
255
256 if firstch == '0' {
257 match src.chars().nth(1) {
258 Some('x' | 'b') => return Some(src),
259 _ => return None,
260 }
261 }
262
263 None
264 }
265
report_bin_hex_error( cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: attr::IntType, size: Size, repr_str: String, val: u128, negative: bool, )266 fn report_bin_hex_error(
267 cx: &LateContext<'_>,
268 expr: &hir::Expr<'_>,
269 ty: attr::IntType,
270 size: Size,
271 repr_str: String,
272 val: u128,
273 negative: bool,
274 ) {
275 let (t, actually) = match ty {
276 attr::IntType::SignedInt(t) => {
277 let actually = if negative {
278 -(size.sign_extend(val) as i128)
279 } else {
280 size.sign_extend(val) as i128
281 };
282 (t.name_str(), actually.to_string())
283 }
284 attr::IntType::UnsignedInt(t) => {
285 let actually = size.truncate(val);
286 (t.name_str(), actually.to_string())
287 }
288 };
289 let sign =
290 if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive };
291 let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map(
292 |suggestion_ty| {
293 if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
294 let (sans_suffix, _) = repr_str.split_at(pos);
295 OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix }
296 } else {
297 OverflowingBinHexSub::Help { suggestion_ty }
298 }
299 },
300 );
301 let sign_bit_sub = (!negative)
302 .then(|| {
303 let ty::Int(int_ty) = cx.typeck_results().node_type(expr.hir_id).kind() else {
304 return None;
305 };
306
307 let Some(bit_width) = int_ty.bit_width() else {
308 return None; // isize case
309 };
310
311 // Skip if sign bit is not set
312 if (val & (1 << (bit_width - 1))) == 0 {
313 return None;
314 }
315
316 let lit_no_suffix =
317 if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
318 repr_str.split_at(pos).0
319 } else {
320 &repr_str
321 };
322
323 Some(OverflowingBinHexSignBitSub {
324 span: expr.span,
325 lit_no_suffix,
326 negative_val: actually.clone(),
327 int_ty: int_ty.name_str(),
328 uint_ty: int_ty.to_unsigned().name_str(),
329 })
330 })
331 .flatten();
332
333 cx.emit_spanned_lint(
334 OVERFLOWING_LITERALS,
335 expr.span,
336 OverflowingBinHex {
337 ty: t,
338 lit: repr_str.clone(),
339 dec: val,
340 actually,
341 sign,
342 sub,
343 sign_bit_sub,
344 },
345 )
346 }
347
348 // This function finds the next fitting type and generates a suggestion string.
349 // It searches for fitting types in the following way (`X < Y`):
350 // - `iX`: if literal fits in `uX` => `uX`, else => `iY`
351 // - `-iX` => `iY`
352 // - `uX` => `uY`
353 //
354 // No suggestion for: `isize`, `usize`.
get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str>355 fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> {
356 use ty::IntTy::*;
357 use ty::UintTy::*;
358 macro_rules! find_fit {
359 ($ty:expr, $val:expr, $negative:expr,
360 $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => {
361 {
362 let _neg = if negative { 1 } else { 0 };
363 match $ty {
364 $($type => {
365 $(if !negative && val <= uint_ty_range($utypes).1 {
366 return Some($utypes.name_str())
367 })*
368 $(if val <= int_ty_range($itypes).1 as u128 + _neg {
369 return Some($itypes.name_str())
370 })*
371 None
372 },)+
373 _ => None
374 }
375 }
376 }
377 }
378 match t.kind() {
379 ty::Int(i) => find_fit!(i, val, negative,
380 I8 => [U8] => [I16, I32, I64, I128],
381 I16 => [U16] => [I32, I64, I128],
382 I32 => [U32] => [I64, I128],
383 I64 => [U64] => [I128],
384 I128 => [U128] => []),
385 ty::Uint(u) => find_fit!(u, val, negative,
386 U8 => [U8, U16, U32, U64, U128] => [],
387 U16 => [U16, U32, U64, U128] => [],
388 U32 => [U32, U64, U128] => [],
389 U64 => [U64, U128] => [],
390 U128 => [U128] => []),
391 _ => None,
392 }
393 }
394
lint_int_literal<'tcx>( cx: &LateContext<'tcx>, type_limits: &TypeLimits, e: &'tcx hir::Expr<'tcx>, lit: &hir::Lit, t: ty::IntTy, v: u128, )395 fn lint_int_literal<'tcx>(
396 cx: &LateContext<'tcx>,
397 type_limits: &TypeLimits,
398 e: &'tcx hir::Expr<'tcx>,
399 lit: &hir::Lit,
400 t: ty::IntTy,
401 v: u128,
402 ) {
403 let int_type = t.normalize(cx.sess().target.pointer_width);
404 let (min, max) = int_ty_range(int_type);
405 let max = max as u128;
406 let negative = type_limits.negated_expr_id == Some(e.hir_id);
407
408 // Detect literal value out of range [min, max] inclusive
409 // avoiding use of -min to prevent overflow/panic
410 if (negative && v > max + 1) || (!negative && v > max) {
411 if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
412 report_bin_hex_error(
413 cx,
414 e,
415 attr::IntType::SignedInt(ty::ast_int_ty(t)),
416 Integer::from_int_ty(cx, t).size(),
417 repr_str,
418 v,
419 negative,
420 );
421 return;
422 }
423
424 if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) {
425 // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`.
426 return;
427 }
428
429 let lit = cx
430 .sess()
431 .source_map()
432 .span_to_snippet(lit.span)
433 .expect("must get snippet from literal");
434 let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
435 .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty });
436
437 cx.emit_spanned_lint(
438 OVERFLOWING_LITERALS,
439 e.span,
440 OverflowingInt { ty: t.name_str(), lit, min, max, help },
441 );
442 }
443 }
444
lint_uint_literal<'tcx>( cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>, lit: &hir::Lit, t: ty::UintTy, )445 fn lint_uint_literal<'tcx>(
446 cx: &LateContext<'tcx>,
447 e: &'tcx hir::Expr<'tcx>,
448 lit: &hir::Lit,
449 t: ty::UintTy,
450 ) {
451 let uint_type = t.normalize(cx.sess().target.pointer_width);
452 let (min, max) = uint_ty_range(uint_type);
453 let lit_val: u128 = match lit.node {
454 // _v is u8, within range by definition
455 ast::LitKind::Byte(_v) => return,
456 ast::LitKind::Int(v, _) => v,
457 _ => bug!(),
458 };
459 if lit_val < min || lit_val > max {
460 let parent_id = cx.tcx.hir().parent_id(e.hir_id);
461 if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) {
462 match par_e.kind {
463 hir::ExprKind::Cast(..) => {
464 if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() {
465 cx.emit_spanned_lint(
466 OVERFLOWING_LITERALS,
467 par_e.span,
468 OnlyCastu8ToChar { span: par_e.span, literal: lit_val },
469 );
470 return;
471 }
472 }
473 _ => {}
474 }
475 }
476 if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) {
477 // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`.
478 return;
479 }
480 if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
481 report_bin_hex_error(
482 cx,
483 e,
484 attr::IntType::UnsignedInt(ty::ast_uint_ty(t)),
485 Integer::from_uint_ty(cx, t).size(),
486 repr_str,
487 lit_val,
488 false,
489 );
490 return;
491 }
492 cx.emit_spanned_lint(
493 OVERFLOWING_LITERALS,
494 e.span,
495 OverflowingUInt {
496 ty: t.name_str(),
497 lit: cx
498 .sess()
499 .source_map()
500 .span_to_snippet(lit.span)
501 .expect("must get snippet from literal"),
502 min,
503 max,
504 },
505 );
506 }
507 }
508
lint_literal<'tcx>( cx: &LateContext<'tcx>, type_limits: &TypeLimits, e: &'tcx hir::Expr<'tcx>, lit: &hir::Lit, )509 fn lint_literal<'tcx>(
510 cx: &LateContext<'tcx>,
511 type_limits: &TypeLimits,
512 e: &'tcx hir::Expr<'tcx>,
513 lit: &hir::Lit,
514 ) {
515 match *cx.typeck_results().node_type(e.hir_id).kind() {
516 ty::Int(t) => {
517 match lit.node {
518 ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => {
519 lint_int_literal(cx, type_limits, e, lit, t, v)
520 }
521 _ => bug!(),
522 };
523 }
524 ty::Uint(t) => lint_uint_literal(cx, e, lit, t),
525 ty::Float(t) => {
526 let is_infinite = match lit.node {
527 ast::LitKind::Float(v, _) => match t {
528 ty::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite),
529 ty::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite),
530 },
531 _ => bug!(),
532 };
533 if is_infinite == Ok(true) {
534 cx.emit_spanned_lint(
535 OVERFLOWING_LITERALS,
536 e.span,
537 OverflowingLiteral {
538 ty: t.name_str(),
539 lit: cx
540 .sess()
541 .source_map()
542 .span_to_snippet(lit.span)
543 .expect("must get snippet from literal"),
544 },
545 );
546 }
547 }
548 _ => {}
549 }
550 }
551
lint_nan<'tcx>( cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>, binop: hir::BinOp, l: &'tcx hir::Expr<'tcx>, r: &'tcx hir::Expr<'tcx>, )552 fn lint_nan<'tcx>(
553 cx: &LateContext<'tcx>,
554 e: &'tcx hir::Expr<'tcx>,
555 binop: hir::BinOp,
556 l: &'tcx hir::Expr<'tcx>,
557 r: &'tcx hir::Expr<'tcx>,
558 ) {
559 fn is_nan(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
560 let expr = expr.peel_blocks().peel_borrows();
561 match expr.kind {
562 ExprKind::Path(qpath) => {
563 let Some(def_id) = cx.typeck_results().qpath_res(&qpath, expr.hir_id).opt_def_id() else { return false; };
564
565 matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::f32_nan | sym::f64_nan))
566 }
567 _ => false,
568 }
569 }
570
571 fn eq_ne(
572 e: &hir::Expr<'_>,
573 l: &hir::Expr<'_>,
574 r: &hir::Expr<'_>,
575 f: impl FnOnce(Span, Span) -> InvalidNanComparisonsSuggestion,
576 ) -> InvalidNanComparisons {
577 let suggestion =
578 if let Some(l_span) = l.span.find_ancestor_inside(e.span) &&
579 let Some(r_span) = r.span.find_ancestor_inside(e.span) {
580 f(l_span, r_span)
581 } else {
582 InvalidNanComparisonsSuggestion::Spanless
583 };
584
585 InvalidNanComparisons::EqNe { suggestion }
586 }
587
588 let lint = match binop.node {
589 hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => {
590 eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
591 nan_plus_binop: l_span.until(r_span),
592 float: r_span.shrink_to_hi(),
593 neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
594 })
595 }
596 hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => {
597 eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
598 nan_plus_binop: l_span.shrink_to_hi().to(r_span),
599 float: l_span.shrink_to_hi(),
600 neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
601 })
602 }
603 hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge
604 if is_nan(cx, l) || is_nan(cx, r) =>
605 {
606 InvalidNanComparisons::LtLeGtGe
607 }
608 _ => return,
609 };
610
611 cx.emit_spanned_lint(INVALID_NAN_COMPARISONS, e.span, lint);
612 }
613
614 impl<'tcx> LateLintPass<'tcx> for TypeLimits {
check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>)615 fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
616 match e.kind {
617 hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => {
618 // propagate negation, if the negation itself isn't negated
619 if self.negated_expr_id != Some(e.hir_id) {
620 self.negated_expr_id = Some(expr.hir_id);
621 }
622 }
623 hir::ExprKind::Binary(binop, ref l, ref r) => {
624 if is_comparison(binop) {
625 if !check_limits(cx, binop, &l, &r) {
626 cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
627 } else {
628 lint_nan(cx, e, binop, l, r);
629 }
630 }
631 }
632 hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit),
633 _ => {}
634 };
635
636 fn is_valid<T: PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
637 match binop.node {
638 hir::BinOpKind::Lt => v > min && v <= max,
639 hir::BinOpKind::Le => v >= min && v < max,
640 hir::BinOpKind::Gt => v >= min && v < max,
641 hir::BinOpKind::Ge => v > min && v <= max,
642 hir::BinOpKind::Eq | hir::BinOpKind::Ne => v >= min && v <= max,
643 _ => bug!(),
644 }
645 }
646
647 fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
648 source_map::respan(
649 binop.span,
650 match binop.node {
651 hir::BinOpKind::Lt => hir::BinOpKind::Gt,
652 hir::BinOpKind::Le => hir::BinOpKind::Ge,
653 hir::BinOpKind::Gt => hir::BinOpKind::Lt,
654 hir::BinOpKind::Ge => hir::BinOpKind::Le,
655 _ => return binop,
656 },
657 )
658 }
659
660 fn check_limits(
661 cx: &LateContext<'_>,
662 binop: hir::BinOp,
663 l: &hir::Expr<'_>,
664 r: &hir::Expr<'_>,
665 ) -> bool {
666 let (lit, expr, swap) = match (&l.kind, &r.kind) {
667 (&hir::ExprKind::Lit(_), _) => (l, r, true),
668 (_, &hir::ExprKind::Lit(_)) => (r, l, false),
669 _ => return true,
670 };
671 // Normalize the binop so that the literal is always on the RHS in
672 // the comparison
673 let norm_binop = if swap { rev_binop(binop) } else { binop };
674 match *cx.typeck_results().node_type(expr.hir_id).kind() {
675 ty::Int(int_ty) => {
676 let (min, max) = int_ty_range(int_ty);
677 let lit_val: i128 = match lit.kind {
678 hir::ExprKind::Lit(ref li) => match li.node {
679 ast::LitKind::Int(
680 v,
681 ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed,
682 ) => v as i128,
683 _ => return true,
684 },
685 _ => bug!(),
686 };
687 is_valid(norm_binop, lit_val, min, max)
688 }
689 ty::Uint(uint_ty) => {
690 let (min, max): (u128, u128) = uint_ty_range(uint_ty);
691 let lit_val: u128 = match lit.kind {
692 hir::ExprKind::Lit(ref li) => match li.node {
693 ast::LitKind::Int(v, _) => v,
694 _ => return true,
695 },
696 _ => bug!(),
697 };
698 is_valid(norm_binop, lit_val, min, max)
699 }
700 _ => true,
701 }
702 }
703
704 fn is_comparison(binop: hir::BinOp) -> bool {
705 matches!(
706 binop.node,
707 hir::BinOpKind::Eq
708 | hir::BinOpKind::Lt
709 | hir::BinOpKind::Le
710 | hir::BinOpKind::Ne
711 | hir::BinOpKind::Ge
712 | hir::BinOpKind::Gt
713 )
714 }
715 }
716 }
717
718 declare_lint! {
719 /// The `improper_ctypes` lint detects incorrect use of types in foreign
720 /// modules.
721 ///
722 /// ### Example
723 ///
724 /// ```rust
725 /// extern "C" {
726 /// static STATIC: String;
727 /// }
728 /// ```
729 ///
730 /// {{produces}}
731 ///
732 /// ### Explanation
733 ///
734 /// The compiler has several checks to verify that types used in `extern`
735 /// blocks are safe and follow certain rules to ensure proper
736 /// compatibility with the foreign interfaces. This lint is issued when it
737 /// detects a probable mistake in a definition. The lint usually should
738 /// provide a description of the issue, along with possibly a hint on how
739 /// to resolve it.
740 IMPROPER_CTYPES,
741 Warn,
742 "proper use of libc types in foreign modules"
743 }
744
745 declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]);
746
747 declare_lint! {
748 /// The `improper_ctypes_definitions` lint detects incorrect use of
749 /// [`extern` function] definitions.
750 ///
751 /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
752 ///
753 /// ### Example
754 ///
755 /// ```rust
756 /// # #![allow(unused)]
757 /// pub extern "C" fn str_type(p: &str) { }
758 /// ```
759 ///
760 /// {{produces}}
761 ///
762 /// ### Explanation
763 ///
764 /// There are many parameter and return types that may be specified in an
765 /// `extern` function that are not compatible with the given ABI. This
766 /// lint is an alert that these types should not be used. The lint usually
767 /// should provide a description of the issue, along with possibly a hint
768 /// on how to resolve it.
769 IMPROPER_CTYPES_DEFINITIONS,
770 Warn,
771 "proper use of libc types in foreign item definitions"
772 }
773
774 declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS]);
775
776 #[derive(Clone, Copy)]
777 pub(crate) enum CItemKind {
778 Declaration,
779 Definition,
780 }
781
782 struct ImproperCTypesVisitor<'a, 'tcx> {
783 cx: &'a LateContext<'tcx>,
784 mode: CItemKind,
785 }
786
787 enum FfiResult<'tcx> {
788 FfiSafe,
789 FfiPhantom(Ty<'tcx>),
790 FfiUnsafe { ty: Ty<'tcx>, reason: DiagnosticMessage, help: Option<DiagnosticMessage> },
791 }
792
nonnull_optimization_guaranteed<'tcx>( tcx: TyCtxt<'tcx>, def: ty::AdtDef<'tcx>, ) -> bool793 pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
794 tcx: TyCtxt<'tcx>,
795 def: ty::AdtDef<'tcx>,
796 ) -> bool {
797 tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed)
798 }
799
800 /// `repr(transparent)` structs can have a single non-ZST field, this function returns that
801 /// field.
transparent_newtype_field<'a, 'tcx>( tcx: TyCtxt<'tcx>, variant: &'a ty::VariantDef, ) -> Option<&'a ty::FieldDef>802 pub fn transparent_newtype_field<'a, 'tcx>(
803 tcx: TyCtxt<'tcx>,
804 variant: &'a ty::VariantDef,
805 ) -> Option<&'a ty::FieldDef> {
806 let param_env = tcx.param_env(variant.def_id);
807 variant.fields.iter().find(|field| {
808 let field_ty = tcx.type_of(field.did).subst_identity();
809 let is_zst = tcx.layout_of(param_env.and(field_ty)).is_ok_and(|layout| layout.is_zst());
810 !is_zst
811 })
812 }
813
814 /// Is type known to be non-null?
ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool815 fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool {
816 let tcx = cx.tcx;
817 match ty.kind() {
818 ty::FnPtr(_) => true,
819 ty::Ref(..) => true,
820 ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true,
821 ty::Adt(def, substs) if def.repr().transparent() && !def.is_union() => {
822 let marked_non_null = nonnull_optimization_guaranteed(tcx, *def);
823
824 if marked_non_null {
825 return true;
826 }
827
828 // `UnsafeCell` has its niche hidden.
829 if def.is_unsafe_cell() {
830 return false;
831 }
832
833 def.variants()
834 .iter()
835 .filter_map(|variant| transparent_newtype_field(cx.tcx, variant))
836 .any(|field| ty_is_known_nonnull(cx, field.ty(tcx, substs), mode))
837 }
838 _ => false,
839 }
840 }
841
842 /// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type.
843 /// If the type passed in was not scalar, returns None.
get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>>844 fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
845 let tcx = cx.tcx;
846 Some(match *ty.kind() {
847 ty::Adt(field_def, field_substs) => {
848 let inner_field_ty = {
849 let mut first_non_zst_ty = field_def
850 .variants()
851 .iter()
852 .filter_map(|v| transparent_newtype_field(cx.tcx, v));
853 debug_assert_eq!(
854 first_non_zst_ty.clone().count(),
855 1,
856 "Wrong number of fields for transparent type"
857 );
858 first_non_zst_ty
859 .next_back()
860 .expect("No non-zst fields in transparent type.")
861 .ty(tcx, field_substs)
862 };
863 return get_nullable_type(cx, inner_field_ty);
864 }
865 ty::Int(ty) => Ty::new_int(tcx, ty),
866 ty::Uint(ty) => Ty::new_uint(tcx, ty),
867 ty::RawPtr(ty_mut) => Ty::new_ptr(tcx, ty_mut),
868 // As these types are always non-null, the nullable equivalent of
869 // Option<T> of these types are their raw pointer counterparts.
870 ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty::TypeAndMut { ty, mutbl }),
871 ty::FnPtr(..) => {
872 // There is no nullable equivalent for Rust's function pointers -- you
873 // must use an Option<fn(..) -> _> to represent it.
874 ty
875 }
876
877 // We should only ever reach this case if ty_is_known_nonnull is extended
878 // to other types.
879 ref unhandled => {
880 debug!(
881 "get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}",
882 unhandled, ty
883 );
884 return None;
885 }
886 })
887 }
888
889 /// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
890 /// can, return the type that `ty` can be safely converted to, otherwise return `None`.
891 /// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`,
892 /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes.
893 /// FIXME: This duplicates code in codegen.
repr_nullable_ptr<'tcx>( cx: &LateContext<'tcx>, ty: Ty<'tcx>, ckind: CItemKind, ) -> Option<Ty<'tcx>>894 pub(crate) fn repr_nullable_ptr<'tcx>(
895 cx: &LateContext<'tcx>,
896 ty: Ty<'tcx>,
897 ckind: CItemKind,
898 ) -> Option<Ty<'tcx>> {
899 debug!("is_repr_nullable_ptr(cx, ty = {:?})", ty);
900 if let ty::Adt(ty_def, substs) = ty.kind() {
901 let field_ty = match &ty_def.variants().raw[..] {
902 [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
903 ([], [field]) | ([field], []) => field.ty(cx.tcx, substs),
904 _ => return None,
905 },
906 _ => return None,
907 };
908
909 if !ty_is_known_nonnull(cx, field_ty, ckind) {
910 return None;
911 }
912
913 // At this point, the field's type is known to be nonnull and the parent enum is Option-like.
914 // If the computed size for the field and the enum are different, the nonnull optimization isn't
915 // being applied (and we've got a problem somewhere).
916 let compute_size_skeleton = |t| SizeSkeleton::compute(t, cx.tcx, cx.param_env).unwrap();
917 if !compute_size_skeleton(ty).same_size(compute_size_skeleton(field_ty)) {
918 bug!("improper_ctypes: Option nonnull optimization not applied?");
919 }
920
921 // Return the nullable type this Option-like enum can be safely represented with.
922 let field_ty_abi = &cx.layout_of(field_ty).unwrap().abi;
923 if let Abi::Scalar(field_ty_scalar) = field_ty_abi {
924 match field_ty_scalar.valid_range(cx) {
925 WrappingRange { start: 0, end }
926 if end == field_ty_scalar.size(&cx.tcx).unsigned_int_max() - 1 =>
927 {
928 return Some(get_nullable_type(cx, field_ty).unwrap());
929 }
930 WrappingRange { start: 1, .. } => {
931 return Some(get_nullable_type(cx, field_ty).unwrap());
932 }
933 WrappingRange { start, end } => {
934 unreachable!("Unhandled start and end range: ({}, {})", start, end)
935 }
936 };
937 }
938 }
939 None
940 }
941
942 impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
943 /// Check if the type is array and emit an unsafe type lint.
check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool944 fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
945 if let ty::Array(..) = ty.kind() {
946 self.emit_ffi_unsafe_type_lint(
947 ty,
948 sp,
949 fluent::lint_improper_ctypes_array_reason,
950 Some(fluent::lint_improper_ctypes_array_help),
951 );
952 true
953 } else {
954 false
955 }
956 }
957
958 /// Checks if the given field's type is "ffi-safe".
check_field_type_for_ffi( &self, cache: &mut FxHashSet<Ty<'tcx>>, field: &ty::FieldDef, substs: SubstsRef<'tcx>, ) -> FfiResult<'tcx>959 fn check_field_type_for_ffi(
960 &self,
961 cache: &mut FxHashSet<Ty<'tcx>>,
962 field: &ty::FieldDef,
963 substs: SubstsRef<'tcx>,
964 ) -> FfiResult<'tcx> {
965 let field_ty = field.ty(self.cx.tcx, substs);
966 let field_ty = self
967 .cx
968 .tcx
969 .try_normalize_erasing_regions(self.cx.param_env, field_ty)
970 .unwrap_or(field_ty);
971 self.check_type_for_ffi(cache, field_ty)
972 }
973
974 /// Checks if the given `VariantDef`'s field types are "ffi-safe".
check_variant_for_ffi( &self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>, def: ty::AdtDef<'tcx>, variant: &ty::VariantDef, substs: SubstsRef<'tcx>, ) -> FfiResult<'tcx>975 fn check_variant_for_ffi(
976 &self,
977 cache: &mut FxHashSet<Ty<'tcx>>,
978 ty: Ty<'tcx>,
979 def: ty::AdtDef<'tcx>,
980 variant: &ty::VariantDef,
981 substs: SubstsRef<'tcx>,
982 ) -> FfiResult<'tcx> {
983 use FfiResult::*;
984
985 let transparent_with_all_zst_fields = if def.repr().transparent() {
986 if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
987 // Transparent newtypes have at most one non-ZST field which needs to be checked..
988 match self.check_field_type_for_ffi(cache, field, substs) {
989 FfiUnsafe { ty, .. } if ty.is_unit() => (),
990 r => return r,
991 }
992
993 false
994 } else {
995 // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
996 // `PhantomData`).
997 true
998 }
999 } else {
1000 false
1001 };
1002
1003 // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
1004 let mut all_phantom = !variant.fields.is_empty();
1005 for field in &variant.fields {
1006 all_phantom &= match self.check_field_type_for_ffi(cache, &field, substs) {
1007 FfiSafe => false,
1008 // `()` fields are FFI-safe!
1009 FfiUnsafe { ty, .. } if ty.is_unit() => false,
1010 FfiPhantom(..) => true,
1011 r @ FfiUnsafe { .. } => return r,
1012 }
1013 }
1014
1015 if all_phantom {
1016 FfiPhantom(ty)
1017 } else if transparent_with_all_zst_fields {
1018 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
1019 } else {
1020 FfiSafe
1021 }
1022 }
1023
1024 /// Checks if the given type is "ffi-safe" (has a stable, well-defined
1025 /// representation which can be exported to C code).
check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult<'tcx>1026 fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult<'tcx> {
1027 use FfiResult::*;
1028
1029 let tcx = self.cx.tcx;
1030
1031 // Protect against infinite recursion, for example
1032 // `struct S(*mut S);`.
1033 // FIXME: A recursion limit is necessary as well, for irregular
1034 // recursive types.
1035 if !cache.insert(ty) {
1036 return FfiSafe;
1037 }
1038
1039 match *ty.kind() {
1040 ty::Adt(def, substs) => {
1041 if def.is_box() && matches!(self.mode, CItemKind::Definition) {
1042 if ty.boxed_ty().is_sized(tcx, self.cx.param_env) {
1043 return FfiSafe;
1044 } else {
1045 return FfiUnsafe {
1046 ty,
1047 reason: fluent::lint_improper_ctypes_box,
1048 help: None,
1049 };
1050 }
1051 }
1052 if def.is_phantom_data() {
1053 return FfiPhantom(ty);
1054 }
1055 match def.adt_kind() {
1056 AdtKind::Struct | AdtKind::Union => {
1057 if !def.repr().c() && !def.repr().transparent() {
1058 return FfiUnsafe {
1059 ty,
1060 reason: if def.is_struct() {
1061 fluent::lint_improper_ctypes_struct_layout_reason
1062 } else {
1063 fluent::lint_improper_ctypes_union_layout_reason
1064 },
1065 help: if def.is_struct() {
1066 Some(fluent::lint_improper_ctypes_struct_layout_help)
1067 } else {
1068 Some(fluent::lint_improper_ctypes_union_layout_help)
1069 },
1070 };
1071 }
1072
1073 let is_non_exhaustive =
1074 def.non_enum_variant().is_field_list_non_exhaustive();
1075 if is_non_exhaustive && !def.did().is_local() {
1076 return FfiUnsafe {
1077 ty,
1078 reason: if def.is_struct() {
1079 fluent::lint_improper_ctypes_struct_non_exhaustive
1080 } else {
1081 fluent::lint_improper_ctypes_union_non_exhaustive
1082 },
1083 help: None,
1084 };
1085 }
1086
1087 if def.non_enum_variant().fields.is_empty() {
1088 return FfiUnsafe {
1089 ty,
1090 reason: if def.is_struct() {
1091 fluent::lint_improper_ctypes_struct_fieldless_reason
1092 } else {
1093 fluent::lint_improper_ctypes_union_fieldless_reason
1094 },
1095 help: if def.is_struct() {
1096 Some(fluent::lint_improper_ctypes_struct_fieldless_help)
1097 } else {
1098 Some(fluent::lint_improper_ctypes_union_fieldless_help)
1099 },
1100 };
1101 }
1102
1103 self.check_variant_for_ffi(cache, ty, def, def.non_enum_variant(), substs)
1104 }
1105 AdtKind::Enum => {
1106 if def.variants().is_empty() {
1107 // Empty enums are okay... although sort of useless.
1108 return FfiSafe;
1109 }
1110
1111 // Check for a repr() attribute to specify the size of the
1112 // discriminant.
1113 if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none()
1114 {
1115 // Special-case types like `Option<extern fn()>`.
1116 if repr_nullable_ptr(self.cx, ty, self.mode).is_none() {
1117 return FfiUnsafe {
1118 ty,
1119 reason: fluent::lint_improper_ctypes_enum_repr_reason,
1120 help: Some(fluent::lint_improper_ctypes_enum_repr_help),
1121 };
1122 }
1123 }
1124
1125 if def.is_variant_list_non_exhaustive() && !def.did().is_local() {
1126 return FfiUnsafe {
1127 ty,
1128 reason: fluent::lint_improper_ctypes_non_exhaustive,
1129 help: None,
1130 };
1131 }
1132
1133 // Check the contained variants.
1134 for variant in def.variants() {
1135 let is_non_exhaustive = variant.is_field_list_non_exhaustive();
1136 if is_non_exhaustive && !variant.def_id.is_local() {
1137 return FfiUnsafe {
1138 ty,
1139 reason: fluent::lint_improper_ctypes_non_exhaustive_variant,
1140 help: None,
1141 };
1142 }
1143
1144 match self.check_variant_for_ffi(cache, ty, def, variant, substs) {
1145 FfiSafe => (),
1146 r => return r,
1147 }
1148 }
1149
1150 FfiSafe
1151 }
1152 }
1153 }
1154
1155 ty::Char => FfiUnsafe {
1156 ty,
1157 reason: fluent::lint_improper_ctypes_char_reason,
1158 help: Some(fluent::lint_improper_ctypes_char_help),
1159 },
1160
1161 ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => {
1162 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None }
1163 }
1164
1165 // Primitive types with a stable representation.
1166 ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe,
1167
1168 ty::Slice(_) => FfiUnsafe {
1169 ty,
1170 reason: fluent::lint_improper_ctypes_slice_reason,
1171 help: Some(fluent::lint_improper_ctypes_slice_help),
1172 },
1173
1174 ty::Dynamic(..) => {
1175 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None }
1176 }
1177
1178 ty::Str => FfiUnsafe {
1179 ty,
1180 reason: fluent::lint_improper_ctypes_str_reason,
1181 help: Some(fluent::lint_improper_ctypes_str_help),
1182 },
1183
1184 ty::Tuple(..) => FfiUnsafe {
1185 ty,
1186 reason: fluent::lint_improper_ctypes_tuple_reason,
1187 help: Some(fluent::lint_improper_ctypes_tuple_help),
1188 },
1189
1190 ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _)
1191 if {
1192 matches!(self.mode, CItemKind::Definition)
1193 && ty.is_sized(self.cx.tcx, self.cx.param_env)
1194 } =>
1195 {
1196 FfiSafe
1197 }
1198
1199 ty::RawPtr(ty::TypeAndMut { ty, .. })
1200 if match ty.kind() {
1201 ty::Tuple(tuple) => tuple.is_empty(),
1202 _ => false,
1203 } =>
1204 {
1205 FfiSafe
1206 }
1207
1208 ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
1209 self.check_type_for_ffi(cache, ty)
1210 }
1211
1212 ty::Array(inner_ty, _) => self.check_type_for_ffi(cache, inner_ty),
1213
1214 ty::FnPtr(sig) => {
1215 if self.is_internal_abi(sig.abi()) {
1216 return FfiUnsafe {
1217 ty,
1218 reason: fluent::lint_improper_ctypes_fnptr_reason,
1219 help: Some(fluent::lint_improper_ctypes_fnptr_help),
1220 };
1221 }
1222
1223 let sig = tcx.erase_late_bound_regions(sig);
1224 for arg in sig.inputs() {
1225 match self.check_type_for_ffi(cache, *arg) {
1226 FfiSafe => {}
1227 r => return r,
1228 }
1229 }
1230
1231 let ret_ty = sig.output();
1232 if ret_ty.is_unit() {
1233 return FfiSafe;
1234 }
1235
1236 self.check_type_for_ffi(cache, ret_ty)
1237 }
1238
1239 ty::Foreign(..) => FfiSafe,
1240
1241 // While opaque types are checked for earlier, if a projection in a struct field
1242 // normalizes to an opaque type, then it will reach this branch.
1243 ty::Alias(ty::Opaque, ..) => {
1244 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None }
1245 }
1246
1247 // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
1248 // so they are currently ignored for the purposes of this lint.
1249 ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
1250 if matches!(self.mode, CItemKind::Definition) =>
1251 {
1252 FfiSafe
1253 }
1254
1255 ty::Param(..)
1256 | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
1257 | ty::Infer(..)
1258 | ty::Bound(..)
1259 | ty::Error(_)
1260 | ty::Closure(..)
1261 | ty::Generator(..)
1262 | ty::GeneratorWitness(..)
1263 | ty::GeneratorWitnessMIR(..)
1264 | ty::Placeholder(..)
1265 | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
1266 }
1267 }
1268
emit_ffi_unsafe_type_lint( &mut self, ty: Ty<'tcx>, sp: Span, note: DiagnosticMessage, help: Option<DiagnosticMessage>, )1269 fn emit_ffi_unsafe_type_lint(
1270 &mut self,
1271 ty: Ty<'tcx>,
1272 sp: Span,
1273 note: DiagnosticMessage,
1274 help: Option<DiagnosticMessage>,
1275 ) {
1276 let lint = match self.mode {
1277 CItemKind::Declaration => IMPROPER_CTYPES,
1278 CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
1279 };
1280 let desc = match self.mode {
1281 CItemKind::Declaration => "block",
1282 CItemKind::Definition => "fn",
1283 };
1284 let span_note = if let ty::Adt(def, _) = ty.kind()
1285 && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) {
1286 Some(sp)
1287 } else {
1288 None
1289 };
1290 self.cx.emit_spanned_lint(
1291 lint,
1292 sp,
1293 ImproperCTypes { ty, desc, label: sp, help, note, span_note },
1294 );
1295 }
1296
check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool1297 fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
1298 struct ProhibitOpaqueTypes;
1299 impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes {
1300 type BreakTy = Ty<'tcx>;
1301
1302 fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
1303 if !ty.has_opaque_types() {
1304 return ControlFlow::Continue(());
1305 }
1306
1307 if let ty::Alias(ty::Opaque, ..) = ty.kind() {
1308 ControlFlow::Break(ty)
1309 } else {
1310 ty.super_visit_with(self)
1311 }
1312 }
1313 }
1314
1315 if let Some(ty) = self
1316 .cx
1317 .tcx
1318 .try_normalize_erasing_regions(self.cx.param_env, ty)
1319 .unwrap_or(ty)
1320 .visit_with(&mut ProhibitOpaqueTypes)
1321 .break_value()
1322 {
1323 self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None);
1324 true
1325 } else {
1326 false
1327 }
1328 }
1329
check_type_for_ffi_and_report_errors( &mut self, sp: Span, ty: Ty<'tcx>, is_static: bool, is_return_type: bool, )1330 fn check_type_for_ffi_and_report_errors(
1331 &mut self,
1332 sp: Span,
1333 ty: Ty<'tcx>,
1334 is_static: bool,
1335 is_return_type: bool,
1336 ) {
1337 if self.check_for_opaque_ty(sp, ty) {
1338 // We've already emitted an error due to an opaque type.
1339 return;
1340 }
1341
1342 let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.param_env, ty).unwrap_or(ty);
1343
1344 // C doesn't really support passing arrays by value - the only way to pass an array by value
1345 // is through a struct. So, first test that the top level isn't an array, and then
1346 // recursively check the types inside.
1347 if !is_static && self.check_for_array_ty(sp, ty) {
1348 return;
1349 }
1350
1351 // Don't report FFI errors for unit return types. This check exists here, and not in
1352 // the caller (where it would make more sense) so that normalization has definitely
1353 // happened.
1354 if is_return_type && ty.is_unit() {
1355 return;
1356 }
1357
1358 match self.check_type_for_ffi(&mut FxHashSet::default(), ty) {
1359 FfiResult::FfiSafe => {}
1360 FfiResult::FfiPhantom(ty) => {
1361 self.emit_ffi_unsafe_type_lint(
1362 ty,
1363 sp,
1364 fluent::lint_improper_ctypes_only_phantomdata,
1365 None,
1366 );
1367 }
1368 FfiResult::FfiUnsafe { ty, reason, help } => {
1369 self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
1370 }
1371 }
1372 }
1373
1374 /// Check if a function's argument types and result type are "ffi-safe".
1375 ///
1376 /// For a external ABI function, argument types and the result type are walked to find fn-ptr
1377 /// types that have external ABIs, as these still need checked.
check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>)1378 fn check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
1379 let sig = self.cx.tcx.fn_sig(def_id).subst_identity();
1380 let sig = self.cx.tcx.erase_late_bound_regions(sig);
1381
1382 for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1383 for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) {
1384 self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, false);
1385 }
1386 }
1387
1388 if let hir::FnRetTy::Return(ref ret_hir) = decl.output {
1389 for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(ret_hir, sig.output()) {
1390 self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, true);
1391 }
1392 }
1393 }
1394
1395 /// Check if a function's argument types and result type are "ffi-safe".
check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>)1396 fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
1397 let sig = self.cx.tcx.fn_sig(def_id).subst_identity();
1398 let sig = self.cx.tcx.erase_late_bound_regions(sig);
1399
1400 for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1401 self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false);
1402 }
1403
1404 if let hir::FnRetTy::Return(ref ret_hir) = decl.output {
1405 self.check_type_for_ffi_and_report_errors(ret_hir.span, sig.output(), false, true);
1406 }
1407 }
1408
check_foreign_static(&mut self, id: hir::OwnerId, span: Span)1409 fn check_foreign_static(&mut self, id: hir::OwnerId, span: Span) {
1410 let ty = self.cx.tcx.type_of(id).subst_identity();
1411 self.check_type_for_ffi_and_report_errors(span, ty, true, false);
1412 }
1413
is_internal_abi(&self, abi: SpecAbi) -> bool1414 fn is_internal_abi(&self, abi: SpecAbi) -> bool {
1415 matches!(
1416 abi,
1417 SpecAbi::Rust | SpecAbi::RustCall | SpecAbi::RustIntrinsic | SpecAbi::PlatformIntrinsic
1418 )
1419 }
1420
1421 /// Find any fn-ptr types with external ABIs in `ty`.
1422 ///
1423 /// For example, `Option<extern "C" fn()>` returns `extern "C" fn()`
find_fn_ptr_ty_with_external_abi( &self, hir_ty: &hir::Ty<'tcx>, ty: Ty<'tcx>, ) -> Vec<(Ty<'tcx>, Span)>1424 fn find_fn_ptr_ty_with_external_abi(
1425 &self,
1426 hir_ty: &hir::Ty<'tcx>,
1427 ty: Ty<'tcx>,
1428 ) -> Vec<(Ty<'tcx>, Span)> {
1429 struct FnPtrFinder<'parent, 'a, 'tcx> {
1430 visitor: &'parent ImproperCTypesVisitor<'a, 'tcx>,
1431 spans: Vec<Span>,
1432 tys: Vec<Ty<'tcx>>,
1433 }
1434
1435 impl<'parent, 'a, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'parent, 'a, 'tcx> {
1436 fn visit_ty(&mut self, ty: &'_ hir::Ty<'_>) {
1437 debug!(?ty);
1438 if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind
1439 && !self.visitor.is_internal_abi(*abi)
1440 {
1441 self.spans.push(ty.span);
1442 }
1443
1444 hir::intravisit::walk_ty(self, ty)
1445 }
1446 }
1447
1448 impl<'vis, 'a, 'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'vis, 'a, 'tcx> {
1449 type BreakTy = Ty<'tcx>;
1450
1451 fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
1452 if let ty::FnPtr(sig) = ty.kind() && !self.visitor.is_internal_abi(sig.abi()) {
1453 self.tys.push(ty);
1454 }
1455
1456 ty.super_visit_with(self)
1457 }
1458 }
1459
1460 let mut visitor = FnPtrFinder { visitor: &*self, spans: Vec::new(), tys: Vec::new() };
1461 ty.visit_with(&mut visitor);
1462 hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty);
1463
1464 iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect()
1465 }
1466 }
1467
1468 impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations {
check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>)1469 fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
1470 let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration };
1471 let abi = cx.tcx.hir().get_foreign_abi(it.hir_id());
1472
1473 match it.kind {
1474 hir::ForeignItemKind::Fn(ref decl, _, _) if !vis.is_internal_abi(abi) => {
1475 vis.check_foreign_fn(it.owner_id.def_id, decl);
1476 }
1477 hir::ForeignItemKind::Static(ref ty, _) if !vis.is_internal_abi(abi) => {
1478 vis.check_foreign_static(it.owner_id, ty.span);
1479 }
1480 hir::ForeignItemKind::Fn(ref decl, _, _) => vis.check_fn(it.owner_id.def_id, decl),
1481 hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
1482 }
1483 }
1484 }
1485
1486 impl ImproperCTypesDefinitions {
check_ty_maybe_containing_foreign_fnptr<'tcx>( &mut self, cx: &LateContext<'tcx>, hir_ty: &'tcx hir::Ty<'_>, ty: Ty<'tcx>, )1487 fn check_ty_maybe_containing_foreign_fnptr<'tcx>(
1488 &mut self,
1489 cx: &LateContext<'tcx>,
1490 hir_ty: &'tcx hir::Ty<'_>,
1491 ty: Ty<'tcx>,
1492 ) {
1493 let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
1494 for (fn_ptr_ty, span) in vis.find_fn_ptr_ty_with_external_abi(hir_ty, ty) {
1495 vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false);
1496 }
1497 }
1498 }
1499
1500 /// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in
1501 /// `extern "C" { }` blocks):
1502 ///
1503 /// - `extern "<abi>" fn` definitions are checked in the same way as the
1504 /// `ImproperCtypesDeclarations` visitor checks functions if `<abi>` is external (e.g. "C").
1505 /// - All other items which contain types (e.g. other functions, struct definitions, etc) are
1506 /// checked for extern fn-ptrs with external ABIs.
1507 impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>)1508 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
1509 match item.kind {
1510 hir::ItemKind::Static(ty, ..)
1511 | hir::ItemKind::Const(ty, ..)
1512 | hir::ItemKind::TyAlias(ty, ..) => {
1513 self.check_ty_maybe_containing_foreign_fnptr(
1514 cx,
1515 ty,
1516 cx.tcx.type_of(item.owner_id).subst_identity(),
1517 );
1518 }
1519 // See `check_fn`..
1520 hir::ItemKind::Fn(..) => {}
1521 // See `check_field_def`..
1522 hir::ItemKind::Union(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) => {}
1523 // Doesn't define something that can contain a external type to be checked.
1524 hir::ItemKind::Impl(..)
1525 | hir::ItemKind::TraitAlias(..)
1526 | hir::ItemKind::Trait(..)
1527 | hir::ItemKind::OpaqueTy(..)
1528 | hir::ItemKind::GlobalAsm(..)
1529 | hir::ItemKind::ForeignMod { .. }
1530 | hir::ItemKind::Mod(..)
1531 | hir::ItemKind::Macro(..)
1532 | hir::ItemKind::Use(..)
1533 | hir::ItemKind::ExternCrate(..) => {}
1534 }
1535 }
1536
check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>)1537 fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
1538 self.check_ty_maybe_containing_foreign_fnptr(
1539 cx,
1540 field.ty,
1541 cx.tcx.type_of(field.def_id).subst_identity(),
1542 );
1543 }
1544
check_fn( &mut self, cx: &LateContext<'tcx>, kind: hir::intravisit::FnKind<'tcx>, decl: &'tcx hir::FnDecl<'_>, _: &'tcx hir::Body<'_>, _: Span, id: LocalDefId, )1545 fn check_fn(
1546 &mut self,
1547 cx: &LateContext<'tcx>,
1548 kind: hir::intravisit::FnKind<'tcx>,
1549 decl: &'tcx hir::FnDecl<'_>,
1550 _: &'tcx hir::Body<'_>,
1551 _: Span,
1552 id: LocalDefId,
1553 ) {
1554 use hir::intravisit::FnKind;
1555
1556 let abi = match kind {
1557 FnKind::ItemFn(_, _, header, ..) => header.abi,
1558 FnKind::Method(_, sig, ..) => sig.header.abi,
1559 _ => return,
1560 };
1561
1562 let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
1563 if vis.is_internal_abi(abi) {
1564 vis.check_fn(id, decl);
1565 } else {
1566 vis.check_foreign_fn(id, decl);
1567 }
1568 }
1569 }
1570
1571 declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]);
1572
1573 impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>)1574 fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
1575 if let hir::ItemKind::Enum(ref enum_definition, _) = it.kind {
1576 let t = cx.tcx.type_of(it.owner_id).subst_identity();
1577 let ty = cx.tcx.erase_regions(t);
1578 let Ok(layout) = cx.layout_of(ty) else { return };
1579 let Variants::Multiple {
1580 tag_encoding: TagEncoding::Direct, tag, ref variants, ..
1581 } = &layout.variants else {
1582 return
1583 };
1584
1585 let tag_size = tag.size(&cx.tcx).bytes();
1586
1587 debug!(
1588 "enum `{}` is {} bytes large with layout:\n{:#?}",
1589 t,
1590 layout.size.bytes(),
1591 layout
1592 );
1593
1594 let (largest, slargest, largest_index) = iter::zip(enum_definition.variants, variants)
1595 .map(|(variant, variant_layout)| {
1596 // Subtract the size of the enum tag.
1597 let bytes = variant_layout.size.bytes().saturating_sub(tag_size);
1598
1599 debug!("- variant `{}` is {} bytes large", variant.ident, bytes);
1600 bytes
1601 })
1602 .enumerate()
1603 .fold((0, 0, 0), |(l, s, li), (idx, size)| {
1604 if size > l {
1605 (size, l, idx)
1606 } else if size > s {
1607 (l, size, li)
1608 } else {
1609 (l, s, li)
1610 }
1611 });
1612
1613 // We only warn if the largest variant is at least thrice as large as
1614 // the second-largest.
1615 if largest > slargest * 3 && slargest > 0 {
1616 cx.emit_spanned_lint(
1617 VARIANT_SIZE_DIFFERENCES,
1618 enum_definition.variants[largest_index].span,
1619 VariantSizeDifferencesDiag { largest },
1620 );
1621 }
1622 }
1623 }
1624 }
1625
1626 declare_lint! {
1627 /// The `invalid_atomic_ordering` lint detects passing an `Ordering`
1628 /// to an atomic operation that does not support that ordering.
1629 ///
1630 /// ### Example
1631 ///
1632 /// ```rust,compile_fail
1633 /// # use core::sync::atomic::{AtomicU8, Ordering};
1634 /// let atom = AtomicU8::new(0);
1635 /// let value = atom.load(Ordering::Release);
1636 /// # let _ = value;
1637 /// ```
1638 ///
1639 /// {{produces}}
1640 ///
1641 /// ### Explanation
1642 ///
1643 /// Some atomic operations are only supported for a subset of the
1644 /// `atomic::Ordering` variants. Passing an unsupported variant will cause
1645 /// an unconditional panic at runtime, which is detected by this lint.
1646 ///
1647 /// This lint will trigger in the following cases: (where `AtomicType` is an
1648 /// atomic type from `core::sync::atomic`, such as `AtomicBool`,
1649 /// `AtomicPtr`, `AtomicUsize`, or any of the other integer atomics).
1650 ///
1651 /// - Passing `Ordering::Acquire` or `Ordering::AcqRel` to
1652 /// `AtomicType::store`.
1653 ///
1654 /// - Passing `Ordering::Release` or `Ordering::AcqRel` to
1655 /// `AtomicType::load`.
1656 ///
1657 /// - Passing `Ordering::Relaxed` to `core::sync::atomic::fence` or
1658 /// `core::sync::atomic::compiler_fence`.
1659 ///
1660 /// - Passing `Ordering::Release` or `Ordering::AcqRel` as the failure
1661 /// ordering for any of `AtomicType::compare_exchange`,
1662 /// `AtomicType::compare_exchange_weak`, or `AtomicType::fetch_update`.
1663 INVALID_ATOMIC_ORDERING,
1664 Deny,
1665 "usage of invalid atomic ordering in atomic operations and memory fences"
1666 }
1667
1668 declare_lint_pass!(InvalidAtomicOrdering => [INVALID_ATOMIC_ORDERING]);
1669
1670 impl InvalidAtomicOrdering {
inherent_atomic_method_call<'hir>( cx: &LateContext<'_>, expr: &Expr<'hir>, recognized_names: &[Symbol], ) -> Option<(Symbol, &'hir [Expr<'hir>])>1671 fn inherent_atomic_method_call<'hir>(
1672 cx: &LateContext<'_>,
1673 expr: &Expr<'hir>,
1674 recognized_names: &[Symbol], // used for fast path calculation
1675 ) -> Option<(Symbol, &'hir [Expr<'hir>])> {
1676 const ATOMIC_TYPES: &[Symbol] = &[
1677 sym::AtomicBool,
1678 sym::AtomicPtr,
1679 sym::AtomicUsize,
1680 sym::AtomicU8,
1681 sym::AtomicU16,
1682 sym::AtomicU32,
1683 sym::AtomicU64,
1684 sym::AtomicU128,
1685 sym::AtomicIsize,
1686 sym::AtomicI8,
1687 sym::AtomicI16,
1688 sym::AtomicI32,
1689 sym::AtomicI64,
1690 sym::AtomicI128,
1691 ];
1692 if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind
1693 && recognized_names.contains(&method_path.ident.name)
1694 && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
1695 && let Some(impl_did) = cx.tcx.impl_of_method(m_def_id)
1696 && let Some(adt) = cx.tcx.type_of(impl_did).subst_identity().ty_adt_def()
1697 // skip extension traits, only lint functions from the standard library
1698 && cx.tcx.trait_id_of_impl(impl_did).is_none()
1699 && let parent = cx.tcx.parent(adt.did())
1700 && cx.tcx.is_diagnostic_item(sym::atomic_mod, parent)
1701 && ATOMIC_TYPES.contains(&cx.tcx.item_name(adt.did()))
1702 {
1703 return Some((method_path.ident.name, args));
1704 }
1705 None
1706 }
1707
match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol>1708 fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol> {
1709 let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None };
1710 let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?;
1711 let tcx = cx.tcx;
1712 let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering);
1713 let name = tcx.item_name(did);
1714 let parent = tcx.parent(did);
1715 [sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find(
1716 |&ordering| {
1717 name == ordering
1718 && (Some(parent) == atomic_ordering
1719 // needed in case this is a ctor, not a variant
1720 || tcx.opt_parent(parent) == atomic_ordering)
1721 },
1722 )
1723 }
1724
check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>)1725 fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
1726 if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
1727 && let Some((ordering_arg, invalid_ordering)) = match method {
1728 sym::load => Some((&args[0], sym::Release)),
1729 sym::store => Some((&args[1], sym::Acquire)),
1730 _ => None,
1731 }
1732 && let Some(ordering) = Self::match_ordering(cx, ordering_arg)
1733 && (ordering == invalid_ordering || ordering == sym::AcqRel)
1734 {
1735 if method == sym::load {
1736 cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad);
1737 } else {
1738 cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore);
1739 };
1740 }
1741 }
1742
check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>)1743 fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
1744 if let ExprKind::Call(ref func, ref args) = expr.kind
1745 && let ExprKind::Path(ref func_qpath) = func.kind
1746 && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
1747 && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
1748 && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
1749 {
1750 cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence);
1751 }
1752 }
1753
check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>)1754 fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
1755 let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak])
1756 else {return };
1757
1758 let fail_order_arg = match method {
1759 sym::fetch_update => &args[1],
1760 sym::compare_exchange | sym::compare_exchange_weak => &args[3],
1761 _ => return,
1762 };
1763
1764 let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
1765
1766 if matches!(fail_ordering, sym::Release | sym::AcqRel) {
1767 cx.emit_spanned_lint(
1768 INVALID_ATOMIC_ORDERING,
1769 fail_order_arg.span,
1770 InvalidAtomicOrderingDiag { method, fail_order_arg_span: fail_order_arg.span },
1771 );
1772 }
1773 }
1774 }
1775
1776 impl<'tcx> LateLintPass<'tcx> for InvalidAtomicOrdering {
check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)1777 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1778 Self::check_atomic_load_store(cx, expr);
1779 Self::check_memory_fence(cx, expr);
1780 Self::check_atomic_compare_exchange(cx, expr);
1781 }
1782 }
1783