rustc_lint/
types.rs

1use std::iter;
2use std::ops::ControlFlow;
3
4use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, VariantIdx, Variants, WrappingRange};
5use rustc_data_structures::fx::FxHashSet;
6use rustc_errors::DiagMessage;
7use rustc_hir::intravisit::VisitorExt;
8use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem};
9use rustc_middle::bug;
10use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton};
11use rustc_middle::ty::{
12    self, Adt, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
13    TypeVisitableExt,
14};
15use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
16use rustc_span::def_id::LocalDefId;
17use rustc_span::{Span, Symbol, source_map, sym};
18use tracing::debug;
19use {rustc_ast as ast, rustc_hir as hir};
20
21mod improper_ctypes;
22
23use crate::lints::{
24    AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
25    AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
26    AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
27    InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
28    UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, UsesPowerAlignment,
29    VariantSizeDifferencesDiag,
30};
31use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
32
33mod literal;
34
35use literal::{int_ty_range, lint_literal, uint_ty_range};
36
37declare_lint! {
38    /// The `unused_comparisons` lint detects comparisons made useless by
39    /// limits of the types involved.
40    ///
41    /// ### Example
42    ///
43    /// ```rust
44    /// fn foo(x: u8) {
45    ///     x >= 0;
46    /// }
47    /// ```
48    ///
49    /// {{produces}}
50    ///
51    /// ### Explanation
52    ///
53    /// A useless comparison may indicate a mistake, and should be fixed or
54    /// removed.
55    UNUSED_COMPARISONS,
56    Warn,
57    "comparisons made useless by limits of the types involved"
58}
59
60declare_lint! {
61    /// The `overflowing_literals` lint detects literal out of range for its
62    /// type.
63    ///
64    /// ### Example
65    ///
66    /// ```rust,compile_fail
67    /// let x: u8 = 1000;
68    /// ```
69    ///
70    /// {{produces}}
71    ///
72    /// ### Explanation
73    ///
74    /// It is usually a mistake to use a literal that overflows the type where
75    /// it is used. Either use a literal that is within range, or change the
76    /// type to be within the range of the literal.
77    OVERFLOWING_LITERALS,
78    Deny,
79    "literal out of range for its type"
80}
81
82declare_lint! {
83    /// The `variant_size_differences` lint detects enums with widely varying
84    /// variant sizes.
85    ///
86    /// ### Example
87    ///
88    /// ```rust,compile_fail
89    /// #![deny(variant_size_differences)]
90    /// enum En {
91    ///     V0(u8),
92    ///     VBig([u8; 1024]),
93    /// }
94    /// ```
95    ///
96    /// {{produces}}
97    ///
98    /// ### Explanation
99    ///
100    /// It can be a mistake to add a variant to an enum that is much larger
101    /// than the other variants, bloating the overall size required for all
102    /// variants. This can impact performance and memory usage. This is
103    /// triggered if one variant is more than 3 times larger than the
104    /// second-largest variant.
105    ///
106    /// Consider placing the large variant's contents on the heap (for example
107    /// via [`Box`]) to keep the overall size of the enum itself down.
108    ///
109    /// This lint is "allow" by default because it can be noisy, and may not be
110    /// an actual problem. Decisions about this should be guided with
111    /// profiling and benchmarking.
112    ///
113    /// [`Box`]: https://doc.rust-lang.org/std/boxed/index.html
114    VARIANT_SIZE_DIFFERENCES,
115    Allow,
116    "detects enums with widely varying variant sizes"
117}
118
119declare_lint! {
120    /// The `invalid_nan_comparisons` lint checks comparison with `f32::NAN` or `f64::NAN`
121    /// as one of the operand.
122    ///
123    /// ### Example
124    ///
125    /// ```rust
126    /// let a = 2.3f32;
127    /// if a == f32::NAN {}
128    /// ```
129    ///
130    /// {{produces}}
131    ///
132    /// ### Explanation
133    ///
134    /// NaN does not compare meaningfully to anything – not
135    /// even itself – so those comparisons are always false.
136    INVALID_NAN_COMPARISONS,
137    Warn,
138    "detects invalid floating point NaN comparisons"
139}
140
141declare_lint! {
142    /// The `ambiguous_wide_pointer_comparisons` lint checks comparison
143    /// of `*const/*mut ?Sized` as the operands.
144    ///
145    /// ### Example
146    ///
147    /// ```rust
148    /// # struct A;
149    /// # struct B;
150    ///
151    /// # trait T {}
152    /// # impl T for A {}
153    /// # impl T for B {}
154    ///
155    /// let ab = (A, B);
156    /// let a = &ab.0 as *const dyn T;
157    /// let b = &ab.1 as *const dyn T;
158    ///
159    /// let _ = a == b;
160    /// ```
161    ///
162    /// {{produces}}
163    ///
164    /// ### Explanation
165    ///
166    /// The comparison includes metadata which may not be expected.
167    AMBIGUOUS_WIDE_POINTER_COMPARISONS,
168    Warn,
169    "detects ambiguous wide pointer comparisons"
170}
171
172declare_lint! {
173    /// The `unpredictable_function_pointer_comparisons` lint checks comparison
174    /// of function pointer as the operands.
175    ///
176    /// ### Example
177    ///
178    /// ```rust
179    /// fn a() {}
180    /// fn b() {}
181    ///
182    /// let f: fn() = a;
183    /// let g: fn() = b;
184    ///
185    /// let _ = f == g;
186    /// ```
187    ///
188    /// {{produces}}
189    ///
190    /// ### Explanation
191    ///
192    /// Function pointers comparisons do not produce meaningful result since
193    /// they are never guaranteed to be unique and could vary between different
194    /// code generation units. Furthermore, different functions could have the
195    /// same address after being merged together.
196    UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
197    Warn,
198    "detects unpredictable function pointer comparisons"
199}
200
201#[derive(Copy, Clone, Default)]
202pub(crate) struct TypeLimits {
203    /// Id of the last visited negated expression
204    negated_expr_id: Option<hir::HirId>,
205    /// Span of the last visited negated expression
206    negated_expr_span: Option<Span>,
207}
208
209impl_lint_pass!(TypeLimits => [
210    UNUSED_COMPARISONS,
211    OVERFLOWING_LITERALS,
212    INVALID_NAN_COMPARISONS,
213    AMBIGUOUS_WIDE_POINTER_COMPARISONS,
214    UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS
215]);
216
217impl TypeLimits {
218    pub(crate) fn new() -> TypeLimits {
219        TypeLimits { negated_expr_id: None, negated_expr_span: None }
220    }
221}
222
223fn lint_nan<'tcx>(
224    cx: &LateContext<'tcx>,
225    e: &'tcx hir::Expr<'tcx>,
226    binop: hir::BinOp,
227    l: &'tcx hir::Expr<'tcx>,
228    r: &'tcx hir::Expr<'tcx>,
229) {
230    fn is_nan(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
231        let expr = expr.peel_blocks().peel_borrows();
232        match expr.kind {
233            ExprKind::Path(qpath) => {
234                let Some(def_id) = cx.typeck_results().qpath_res(&qpath, expr.hir_id).opt_def_id()
235                else {
236                    return false;
237                };
238
239                matches!(
240                    cx.tcx.get_diagnostic_name(def_id),
241                    Some(sym::f16_nan | sym::f32_nan | sym::f64_nan | sym::f128_nan)
242                )
243            }
244            _ => false,
245        }
246    }
247
248    fn eq_ne(
249        e: &hir::Expr<'_>,
250        l: &hir::Expr<'_>,
251        r: &hir::Expr<'_>,
252        f: impl FnOnce(Span, Span) -> InvalidNanComparisonsSuggestion,
253    ) -> InvalidNanComparisons {
254        let suggestion = if let Some(l_span) = l.span.find_ancestor_inside(e.span)
255            && let Some(r_span) = r.span.find_ancestor_inside(e.span)
256        {
257            f(l_span, r_span)
258        } else {
259            InvalidNanComparisonsSuggestion::Spanless
260        };
261
262        InvalidNanComparisons::EqNe { suggestion }
263    }
264
265    let lint = match binop.node {
266        hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => {
267            eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
268                nan_plus_binop: l_span.until(r_span),
269                float: r_span.shrink_to_hi(),
270                neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
271            })
272        }
273        hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => {
274            eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
275                nan_plus_binop: l_span.shrink_to_hi().to(r_span),
276                float: l_span.shrink_to_hi(),
277                neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
278            })
279        }
280        hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge
281            if is_nan(cx, l) || is_nan(cx, r) =>
282        {
283            InvalidNanComparisons::LtLeGtGe
284        }
285        _ => return,
286    };
287
288    cx.emit_span_lint(INVALID_NAN_COMPARISONS, e.span, lint);
289}
290
291#[derive(Debug, PartialEq, Copy, Clone)]
292enum ComparisonOp {
293    BinOp(hir::BinOpKind),
294    Other,
295}
296
297fn lint_wide_pointer<'tcx>(
298    cx: &LateContext<'tcx>,
299    e: &'tcx hir::Expr<'tcx>,
300    cmpop: ComparisonOp,
301    l: &'tcx hir::Expr<'tcx>,
302    r: &'tcx hir::Expr<'tcx>,
303) {
304    let ptr_unsized = |mut ty: Ty<'tcx>| -> Option<(
305        /* number of refs */ usize,
306        /* modifiers */ String,
307        /* is dyn */ bool,
308    )> {
309        let mut refs = 0;
310        // here we remove any "implicit" references and count the number
311        // of them to correctly suggest the right number of deref
312        while let ty::Ref(_, inner_ty, _) = ty.kind() {
313            ty = *inner_ty;
314            refs += 1;
315        }
316
317        // get the inner type of a pointer (or akin)
318        let mut modifiers = String::new();
319        ty = match ty.kind() {
320            ty::RawPtr(ty, _) => *ty,
321            ty::Adt(def, args) if cx.tcx.is_diagnostic_item(sym::NonNull, def.did()) => {
322                modifiers.push_str(".as_ptr()");
323                args.type_at(0)
324            }
325            _ => return None,
326        };
327
328        (!ty.is_sized(cx.tcx, cx.typing_env()))
329            .then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn))))
330    };
331
332    // the left and right operands can have references, remove any explicit references
333    let l = l.peel_borrows();
334    let r = r.peel_borrows();
335
336    let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else {
337        return;
338    };
339    let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else {
340        return;
341    };
342
343    let Some((l_ty_refs, l_modifiers, l_inner_ty_is_dyn)) = ptr_unsized(l_ty) else {
344        return;
345    };
346    let Some((r_ty_refs, r_modifiers, r_inner_ty_is_dyn)) = ptr_unsized(r_ty) else {
347        return;
348    };
349
350    let (Some(l_span), Some(r_span)) =
351        (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
352    else {
353        return cx.emit_span_lint(
354            AMBIGUOUS_WIDE_POINTER_COMPARISONS,
355            e.span,
356            AmbiguousWidePointerComparisons::Spanless,
357        );
358    };
359
360    let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
361    let is_eq_ne = matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne));
362    let is_dyn_comparison = l_inner_ty_is_dyn && r_inner_ty_is_dyn;
363
364    let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
365    let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
366    let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
367
368    let deref_left = &*"*".repeat(l_ty_refs);
369    let deref_right = &*"*".repeat(r_ty_refs);
370
371    let l_modifiers = &*l_modifiers;
372    let r_modifiers = &*r_modifiers;
373
374    cx.emit_span_lint(
375        AMBIGUOUS_WIDE_POINTER_COMPARISONS,
376        e.span,
377        AmbiguousWidePointerComparisons::Spanful {
378            addr_metadata_suggestion: (is_eq_ne && !is_dyn_comparison).then(|| {
379                AmbiguousWidePointerComparisonsAddrMetadataSuggestion {
380                    ne,
381                    deref_left,
382                    deref_right,
383                    l_modifiers,
384                    r_modifiers,
385                    left,
386                    middle,
387                    right,
388                }
389            }),
390            addr_suggestion: if is_eq_ne {
391                AmbiguousWidePointerComparisonsAddrSuggestion::AddrEq {
392                    ne,
393                    deref_left,
394                    deref_right,
395                    l_modifiers,
396                    r_modifiers,
397                    left,
398                    middle,
399                    right,
400                }
401            } else {
402                AmbiguousWidePointerComparisonsAddrSuggestion::Cast {
403                    deref_left,
404                    deref_right,
405                    l_modifiers,
406                    r_modifiers,
407                    paren_left: if l_ty_refs != 0 { ")" } else { "" },
408                    paren_right: if r_ty_refs != 0 { ")" } else { "" },
409                    left_before: (l_ty_refs != 0).then_some(l_span.shrink_to_lo()),
410                    left_after: l_span.shrink_to_hi(),
411                    right_before: (r_ty_refs != 0).then_some(r_span.shrink_to_lo()),
412                    right_after: r_span.shrink_to_hi(),
413                }
414            },
415        },
416    );
417}
418
419fn lint_fn_pointer<'tcx>(
420    cx: &LateContext<'tcx>,
421    e: &'tcx hir::Expr<'tcx>,
422    cmpop: ComparisonOp,
423    l: &'tcx hir::Expr<'tcx>,
424    r: &'tcx hir::Expr<'tcx>,
425) {
426    let peel_refs = |mut ty: Ty<'tcx>| -> (Ty<'tcx>, usize) {
427        let mut refs = 0;
428
429        while let ty::Ref(_, inner_ty, _) = ty.kind() {
430            ty = *inner_ty;
431            refs += 1;
432        }
433
434        (ty, refs)
435    };
436
437    // Left and right operands can have borrows, remove them
438    let l = l.peel_borrows();
439    let r = r.peel_borrows();
440
441    let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else { return };
442    let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else { return };
443
444    // Remove any references as `==` will deref through them (and count the
445    // number of references removed, for latter).
446    let (l_ty, l_ty_refs) = peel_refs(l_ty);
447    let (r_ty, r_ty_refs) = peel_refs(r_ty);
448
449    if l_ty.is_fn() && r_ty.is_fn() {
450        // both operands are function pointers, fallthrough
451    } else if let ty::Adt(l_def, l_args) = l_ty.kind()
452        && let ty::Adt(r_def, r_args) = r_ty.kind()
453        && cx.tcx.is_lang_item(l_def.did(), LangItem::Option)
454        && cx.tcx.is_lang_item(r_def.did(), LangItem::Option)
455        && let Some(l_some_arg) = l_args.get(0)
456        && let Some(r_some_arg) = r_args.get(0)
457        && l_some_arg.expect_ty().is_fn()
458        && r_some_arg.expect_ty().is_fn()
459    {
460        // both operands are `Option<{function ptr}>`
461        return cx.emit_span_lint(
462            UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
463            e.span,
464            UnpredictableFunctionPointerComparisons::Warn,
465        );
466    } else {
467        // types are not function pointers, nothing to do
468        return;
469    }
470
471    // Let's try to suggest `ptr::fn_addr_eq` if/when possible.
472
473    let is_eq_ne = matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne));
474
475    if !is_eq_ne {
476        // Neither `==` nor `!=`, we can't suggest `ptr::fn_addr_eq`, just show the warning.
477        return cx.emit_span_lint(
478            UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
479            e.span,
480            UnpredictableFunctionPointerComparisons::Warn,
481        );
482    }
483
484    let (Some(l_span), Some(r_span)) =
485        (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
486    else {
487        // No appropriate spans for the left and right operands, just show the warning.
488        return cx.emit_span_lint(
489            UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
490            e.span,
491            UnpredictableFunctionPointerComparisons::Warn,
492        );
493    };
494
495    let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
496
497    // `ptr::fn_addr_eq` only works with raw pointer, deref any references.
498    let deref_left = &*"*".repeat(l_ty_refs);
499    let deref_right = &*"*".repeat(r_ty_refs);
500
501    let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
502    let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
503    let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
504
505    let sugg =
506        // We only check for a right cast as `FnDef` == `FnPtr` is not possible,
507        // only `FnPtr == FnDef` is possible.
508        if !r_ty.is_fn_ptr() {
509            let fn_sig = r_ty.fn_sig(cx.tcx);
510
511            UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEqWithCast {
512                ne,
513                fn_sig,
514                deref_left,
515                deref_right,
516                left,
517                middle,
518                right,
519            }
520        } else {
521            UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEq {
522                ne,
523                deref_left,
524                deref_right,
525                left,
526                middle,
527                right,
528            }
529        };
530
531    cx.emit_span_lint(
532        UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
533        e.span,
534        UnpredictableFunctionPointerComparisons::Suggestion { sugg },
535    );
536}
537
538impl<'tcx> LateLintPass<'tcx> for TypeLimits {
539    fn check_lit(
540        &mut self,
541        cx: &LateContext<'tcx>,
542        hir_id: HirId,
543        lit: &'tcx hir::Lit,
544        negated: bool,
545    ) {
546        if negated {
547            self.negated_expr_id = Some(hir_id);
548            self.negated_expr_span = Some(lit.span);
549        }
550        lint_literal(cx, self, hir_id, lit.span, lit, negated);
551    }
552
553    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
554        match e.kind {
555            hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
556                // Propagate negation, if the negation itself isn't negated
557                if self.negated_expr_id != Some(e.hir_id) {
558                    self.negated_expr_id = Some(expr.hir_id);
559                    self.negated_expr_span = Some(e.span);
560                }
561            }
562            hir::ExprKind::Binary(binop, ref l, ref r) => {
563                if is_comparison(binop) {
564                    if !check_limits(cx, binop, l, r) {
565                        cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
566                    } else {
567                        lint_nan(cx, e, binop, l, r);
568                        let cmpop = ComparisonOp::BinOp(binop.node);
569                        lint_wide_pointer(cx, e, cmpop, l, r);
570                        lint_fn_pointer(cx, e, cmpop, l, r);
571                    }
572                }
573            }
574            hir::ExprKind::Call(path, [l, r])
575                if let ExprKind::Path(ref qpath) = path.kind
576                    && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
577                    && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
578                    && let Some(cmpop) = diag_item_cmpop(diag_item) =>
579            {
580                lint_wide_pointer(cx, e, cmpop, l, r);
581                lint_fn_pointer(cx, e, cmpop, l, r);
582            }
583            hir::ExprKind::MethodCall(_, l, [r], _)
584                if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
585                    && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
586                    && let Some(cmpop) = diag_item_cmpop(diag_item) =>
587            {
588                lint_wide_pointer(cx, e, cmpop, l, r);
589                lint_fn_pointer(cx, e, cmpop, l, r);
590            }
591            _ => {}
592        };
593
594        fn is_valid<T: PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
595            match binop.node {
596                hir::BinOpKind::Lt => v > min && v <= max,
597                hir::BinOpKind::Le => v >= min && v < max,
598                hir::BinOpKind::Gt => v >= min && v < max,
599                hir::BinOpKind::Ge => v > min && v <= max,
600                hir::BinOpKind::Eq | hir::BinOpKind::Ne => v >= min && v <= max,
601                _ => bug!(),
602            }
603        }
604
605        fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
606            source_map::respan(
607                binop.span,
608                match binop.node {
609                    hir::BinOpKind::Lt => hir::BinOpKind::Gt,
610                    hir::BinOpKind::Le => hir::BinOpKind::Ge,
611                    hir::BinOpKind::Gt => hir::BinOpKind::Lt,
612                    hir::BinOpKind::Ge => hir::BinOpKind::Le,
613                    _ => return binop,
614                },
615            )
616        }
617
618        fn check_limits(
619            cx: &LateContext<'_>,
620            binop: hir::BinOp,
621            l: &hir::Expr<'_>,
622            r: &hir::Expr<'_>,
623        ) -> bool {
624            let (lit, expr, swap) = match (&l.kind, &r.kind) {
625                (&hir::ExprKind::Lit(_), _) => (l, r, true),
626                (_, &hir::ExprKind::Lit(_)) => (r, l, false),
627                _ => return true,
628            };
629            // Normalize the binop so that the literal is always on the RHS in
630            // the comparison
631            let norm_binop = if swap { rev_binop(binop) } else { binop };
632            match *cx.typeck_results().node_type(expr.hir_id).kind() {
633                ty::Int(int_ty) => {
634                    let (min, max) = int_ty_range(int_ty);
635                    let lit_val: i128 = match lit.kind {
636                        hir::ExprKind::Lit(li) => match li.node {
637                            ast::LitKind::Int(
638                                v,
639                                ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed,
640                            ) => v.get() as i128,
641                            _ => return true,
642                        },
643                        _ => bug!(),
644                    };
645                    is_valid(norm_binop, lit_val, min, max)
646                }
647                ty::Uint(uint_ty) => {
648                    let (min, max): (u128, u128) = uint_ty_range(uint_ty);
649                    let lit_val: u128 = match lit.kind {
650                        hir::ExprKind::Lit(li) => match li.node {
651                            ast::LitKind::Int(v, _) => v.get(),
652                            _ => return true,
653                        },
654                        _ => bug!(),
655                    };
656                    is_valid(norm_binop, lit_val, min, max)
657                }
658                _ => true,
659            }
660        }
661
662        fn is_comparison(binop: hir::BinOp) -> bool {
663            matches!(
664                binop.node,
665                hir::BinOpKind::Eq
666                    | hir::BinOpKind::Lt
667                    | hir::BinOpKind::Le
668                    | hir::BinOpKind::Ne
669                    | hir::BinOpKind::Ge
670                    | hir::BinOpKind::Gt
671            )
672        }
673
674        fn diag_item_cmpop(diag_item: Symbol) -> Option<ComparisonOp> {
675            Some(match diag_item {
676                sym::cmp_ord_max => ComparisonOp::Other,
677                sym::cmp_ord_min => ComparisonOp::Other,
678                sym::ord_cmp_method => ComparisonOp::Other,
679                sym::cmp_partialeq_eq => ComparisonOp::BinOp(hir::BinOpKind::Eq),
680                sym::cmp_partialeq_ne => ComparisonOp::BinOp(hir::BinOpKind::Ne),
681                sym::cmp_partialord_cmp => ComparisonOp::Other,
682                sym::cmp_partialord_ge => ComparisonOp::BinOp(hir::BinOpKind::Ge),
683                sym::cmp_partialord_gt => ComparisonOp::BinOp(hir::BinOpKind::Gt),
684                sym::cmp_partialord_le => ComparisonOp::BinOp(hir::BinOpKind::Le),
685                sym::cmp_partialord_lt => ComparisonOp::BinOp(hir::BinOpKind::Lt),
686                _ => return None,
687            })
688        }
689    }
690}
691
692declare_lint! {
693    /// The `improper_ctypes` lint detects incorrect use of types in foreign
694    /// modules.
695    ///
696    /// ### Example
697    ///
698    /// ```rust
699    /// extern "C" {
700    ///     static STATIC: String;
701    /// }
702    /// ```
703    ///
704    /// {{produces}}
705    ///
706    /// ### Explanation
707    ///
708    /// The compiler has several checks to verify that types used in `extern`
709    /// blocks are safe and follow certain rules to ensure proper
710    /// compatibility with the foreign interfaces. This lint is issued when it
711    /// detects a probable mistake in a definition. The lint usually should
712    /// provide a description of the issue, along with possibly a hint on how
713    /// to resolve it.
714    IMPROPER_CTYPES,
715    Warn,
716    "proper use of libc types in foreign modules"
717}
718
719declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]);
720
721declare_lint! {
722    /// The `improper_ctypes_definitions` lint detects incorrect use of
723    /// [`extern` function] definitions.
724    ///
725    /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
726    ///
727    /// ### Example
728    ///
729    /// ```rust
730    /// # #![allow(unused)]
731    /// pub extern "C" fn str_type(p: &str) { }
732    /// ```
733    ///
734    /// {{produces}}
735    ///
736    /// ### Explanation
737    ///
738    /// There are many parameter and return types that may be specified in an
739    /// `extern` function that are not compatible with the given ABI. This
740    /// lint is an alert that these types should not be used. The lint usually
741    /// should provide a description of the issue, along with possibly a hint
742    /// on how to resolve it.
743    IMPROPER_CTYPES_DEFINITIONS,
744    Warn,
745    "proper use of libc types in foreign item definitions"
746}
747
748declare_lint! {
749    /// The `uses_power_alignment` lint detects specific `repr(C)`
750    /// aggregates on AIX.
751    /// In its platform C ABI, AIX uses the "power" (as in PowerPC) alignment
752    /// rule (detailed in https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=data-using-alignment-modes#alignment),
753    /// which can also be set for XLC by `#pragma align(power)` or
754    /// `-qalign=power`. Aggregates with a floating-point type as the
755    /// recursively first field (as in "at offset 0") modify the layout of
756    /// *subsequent* fields of the associated structs to use an alignment value
757    /// where the floating-point type is aligned on a 4-byte boundary.
758    ///
759    /// The power alignment rule for structs needed for C compatibility is
760    /// unimplementable within `repr(C)` in the compiler without building in
761    /// handling of references to packed fields and infectious nested layouts,
762    /// so a warning is produced in these situations.
763    ///
764    /// ### Example
765    ///
766    /// ```rust,ignore (fails on non-powerpc64-ibm-aix)
767    /// #[repr(C)]
768    /// pub struct Floats {
769    ///     a: f64,
770    ///     b: u8,
771    ///     c: f64,
772    /// }
773    /// ```
774    ///
775    /// This will produce:
776    ///
777    /// ```text
778    /// warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type
779    ///  --> <source>:5:3
780    ///   |
781    /// 5 |   c: f64,
782    ///   |   ^^^^^^
783    ///   |
784    ///   = note: `#[warn(uses_power_alignment)]` on by default
785    /// ```
786    ///
787    /// ### Explanation
788    ///
789    /// The power alignment rule specifies that the above struct has the
790    /// following alignment:
791    ///  - offset_of!(Floats, a) == 0
792    ///  - offset_of!(Floats, b) == 8
793    ///  - offset_of!(Floats, c) == 12
794    /// However, rust currently aligns `c` at offset_of!(Floats, c) == 16.
795    /// Thus, a warning should be produced for the above struct in this case.
796    USES_POWER_ALIGNMENT,
797    Warn,
798    "Structs do not follow the power alignment rule under repr(C)"
799}
800
801declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS, USES_POWER_ALIGNMENT]);
802
803#[derive(Clone, Copy)]
804pub(crate) enum CItemKind {
805    Declaration,
806    Definition,
807}
808
809struct ImproperCTypesVisitor<'a, 'tcx> {
810    cx: &'a LateContext<'tcx>,
811    mode: CItemKind,
812}
813
814/// Accumulator for recursive ffi type checking
815struct CTypesVisitorState<'tcx> {
816    cache: FxHashSet<Ty<'tcx>>,
817    /// The original type being checked, before we recursed
818    /// to any other types it contains.
819    base_ty: Ty<'tcx>,
820}
821
822enum FfiResult<'tcx> {
823    FfiSafe,
824    FfiPhantom(Ty<'tcx>),
825    FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> },
826}
827
828pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
829    tcx: TyCtxt<'tcx>,
830    def: ty::AdtDef<'tcx>,
831) -> bool {
832    tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed)
833}
834
835/// `repr(transparent)` structs can have a single non-1-ZST field, this function returns that
836/// field.
837pub(crate) fn transparent_newtype_field<'a, 'tcx>(
838    tcx: TyCtxt<'tcx>,
839    variant: &'a ty::VariantDef,
840) -> Option<&'a ty::FieldDef> {
841    let typing_env = ty::TypingEnv::non_body_analysis(tcx, variant.def_id);
842    variant.fields.iter().find(|field| {
843        let field_ty = tcx.type_of(field.did).instantiate_identity();
844        let is_1zst =
845            tcx.layout_of(typing_env.as_query_input(field_ty)).is_ok_and(|layout| layout.is_1zst());
846        !is_1zst
847    })
848}
849
850/// Is type known to be non-null?
851fn ty_is_known_nonnull<'tcx>(
852    tcx: TyCtxt<'tcx>,
853    typing_env: ty::TypingEnv<'tcx>,
854    ty: Ty<'tcx>,
855    mode: CItemKind,
856) -> bool {
857    let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
858
859    match ty.kind() {
860        ty::FnPtr(..) => true,
861        ty::Ref(..) => true,
862        ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true,
863        ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => {
864            let marked_non_null = nonnull_optimization_guaranteed(tcx, *def);
865
866            if marked_non_null {
867                return true;
868            }
869
870            // `UnsafeCell` has its niche hidden.
871            if def.is_unsafe_cell() {
872                return false;
873            }
874
875            def.variants()
876                .iter()
877                .filter_map(|variant| transparent_newtype_field(tcx, variant))
878                .any(|field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args), mode))
879        }
880        ty::Pat(base, pat) => {
881            ty_is_known_nonnull(tcx, typing_env, *base, mode)
882                || Option::unwrap_or_default(
883                    try {
884                        match **pat {
885                            ty::PatternKind::Range { start, end, include_end } => {
886                                match (start, end) {
887                                    (Some(start), None) => {
888                                        start.try_to_value()?.try_to_bits(tcx, typing_env)? > 0
889                                    }
890                                    (Some(start), Some(end)) => {
891                                        let start =
892                                            start.try_to_value()?.try_to_bits(tcx, typing_env)?;
893                                        let end =
894                                            end.try_to_value()?.try_to_bits(tcx, typing_env)?;
895
896                                        if include_end {
897                                            // This also works for negative numbers, as we just need
898                                            // to ensure we aren't wrapping over zero.
899                                            start > 0 && end >= start
900                                        } else {
901                                            start > 0 && end > start
902                                        }
903                                    }
904                                    _ => false,
905                                }
906                            }
907                        }
908                    },
909                )
910        }
911        _ => false,
912    }
913}
914
915/// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type.
916/// If the type passed in was not scalar, returns None.
917fn get_nullable_type<'tcx>(
918    tcx: TyCtxt<'tcx>,
919    typing_env: ty::TypingEnv<'tcx>,
920    ty: Ty<'tcx>,
921) -> Option<Ty<'tcx>> {
922    let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
923
924    Some(match *ty.kind() {
925        ty::Adt(field_def, field_args) => {
926            let inner_field_ty = {
927                let mut first_non_zst_ty =
928                    field_def.variants().iter().filter_map(|v| transparent_newtype_field(tcx, v));
929                debug_assert_eq!(
930                    first_non_zst_ty.clone().count(),
931                    1,
932                    "Wrong number of fields for transparent type"
933                );
934                first_non_zst_ty
935                    .next_back()
936                    .expect("No non-zst fields in transparent type.")
937                    .ty(tcx, field_args)
938            };
939            return get_nullable_type(tcx, typing_env, inner_field_ty);
940        }
941        ty::Pat(base, ..) => return get_nullable_type(tcx, typing_env, base),
942        ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => ty,
943        // As these types are always non-null, the nullable equivalent of
944        // `Option<T>` of these types are their raw pointer counterparts.
945        ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl),
946        // There is no nullable equivalent for Rust's function pointers,
947        // you must use an `Option<fn(..) -> _>` to represent it.
948        ty::FnPtr(..) => ty,
949        // We should only ever reach this case if `ty_is_known_nonnull` is
950        // extended to other types.
951        ref unhandled => {
952            debug!(
953                "get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}",
954                unhandled, ty
955            );
956            return None;
957        }
958    })
959}
960
961/// A type is niche-optimization candidate iff:
962/// - Is a zero-sized type with alignment 1 (a “1-ZST”).
963/// - Has no fields.
964/// - Does not have the `#[non_exhaustive]` attribute.
965fn is_niche_optimization_candidate<'tcx>(
966    tcx: TyCtxt<'tcx>,
967    typing_env: ty::TypingEnv<'tcx>,
968    ty: Ty<'tcx>,
969) -> bool {
970    if tcx.layout_of(typing_env.as_query_input(ty)).is_ok_and(|layout| !layout.is_1zst()) {
971        return false;
972    }
973
974    match ty.kind() {
975        ty::Adt(ty_def, _) => {
976            let non_exhaustive = ty_def.is_variant_list_non_exhaustive();
977            let empty = (ty_def.is_struct() && ty_def.all_fields().next().is_none())
978                || (ty_def.is_enum() && ty_def.variants().is_empty());
979
980            !non_exhaustive && empty
981        }
982        ty::Tuple(tys) => tys.is_empty(),
983        _ => false,
984    }
985}
986
987/// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
988/// can, return the type that `ty` can be safely converted to, otherwise return `None`.
989/// Currently restricted to function pointers, boxes, references, `core::num::NonZero`,
990/// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes.
991pub(crate) fn repr_nullable_ptr<'tcx>(
992    tcx: TyCtxt<'tcx>,
993    typing_env: ty::TypingEnv<'tcx>,
994    ty: Ty<'tcx>,
995    ckind: CItemKind,
996) -> Option<Ty<'tcx>> {
997    debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty);
998    match ty.kind() {
999        ty::Adt(ty_def, args) => {
1000            let field_ty = match &ty_def.variants().raw[..] {
1001                [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
1002                    ([], [field]) | ([field], []) => field.ty(tcx, args),
1003                    ([field1], [field2]) => {
1004                        let ty1 = field1.ty(tcx, args);
1005                        let ty2 = field2.ty(tcx, args);
1006
1007                        if is_niche_optimization_candidate(tcx, typing_env, ty1) {
1008                            ty2
1009                        } else if is_niche_optimization_candidate(tcx, typing_env, ty2) {
1010                            ty1
1011                        } else {
1012                            return None;
1013                        }
1014                    }
1015                    _ => return None,
1016                },
1017                _ => return None,
1018            };
1019
1020            if !ty_is_known_nonnull(tcx, typing_env, field_ty, ckind) {
1021                return None;
1022            }
1023
1024            // At this point, the field's type is known to be nonnull and the parent enum is Option-like.
1025            // If the computed size for the field and the enum are different, the nonnull optimization isn't
1026            // being applied (and we've got a problem somewhere).
1027            let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, typing_env).ok();
1028            if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) {
1029                bug!("improper_ctypes: Option nonnull optimization not applied?");
1030            }
1031
1032            // Return the nullable type this Option-like enum can be safely represented with.
1033            let field_ty_layout = tcx.layout_of(typing_env.as_query_input(field_ty));
1034            if field_ty_layout.is_err() && !field_ty.has_non_region_param() {
1035                bug!("should be able to compute the layout of non-polymorphic type");
1036            }
1037
1038            let field_ty_abi = &field_ty_layout.ok()?.backend_repr;
1039            if let BackendRepr::Scalar(field_ty_scalar) = field_ty_abi {
1040                match field_ty_scalar.valid_range(&tcx) {
1041                    WrappingRange { start: 0, end }
1042                        if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 =>
1043                    {
1044                        return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap());
1045                    }
1046                    WrappingRange { start: 1, .. } => {
1047                        return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap());
1048                    }
1049                    WrappingRange { start, end } => {
1050                        unreachable!("Unhandled start and end range: ({}, {})", start, end)
1051                    }
1052                };
1053            }
1054            None
1055        }
1056        ty::Pat(base, pat) => match **pat {
1057            ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, *base),
1058        },
1059        _ => None,
1060    }
1061}
1062
1063impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1064    /// Check if the type is array and emit an unsafe type lint.
1065    fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
1066        if let ty::Array(..) = ty.kind() {
1067            self.emit_ffi_unsafe_type_lint(
1068                ty,
1069                sp,
1070                fluent::lint_improper_ctypes_array_reason,
1071                Some(fluent::lint_improper_ctypes_array_help),
1072            );
1073            true
1074        } else {
1075            false
1076        }
1077    }
1078
1079    /// Checks if the given field's type is "ffi-safe".
1080    fn check_field_type_for_ffi(
1081        &self,
1082        acc: &mut CTypesVisitorState<'tcx>,
1083        field: &ty::FieldDef,
1084        args: GenericArgsRef<'tcx>,
1085    ) -> FfiResult<'tcx> {
1086        let field_ty = field.ty(self.cx.tcx, args);
1087        let field_ty = self
1088            .cx
1089            .tcx
1090            .try_normalize_erasing_regions(self.cx.typing_env(), field_ty)
1091            .unwrap_or(field_ty);
1092        self.check_type_for_ffi(acc, field_ty)
1093    }
1094
1095    /// Checks if the given `VariantDef`'s field types are "ffi-safe".
1096    fn check_variant_for_ffi(
1097        &self,
1098        acc: &mut CTypesVisitorState<'tcx>,
1099        ty: Ty<'tcx>,
1100        def: ty::AdtDef<'tcx>,
1101        variant: &ty::VariantDef,
1102        args: GenericArgsRef<'tcx>,
1103    ) -> FfiResult<'tcx> {
1104        use FfiResult::*;
1105        let transparent_with_all_zst_fields = if def.repr().transparent() {
1106            if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
1107                // Transparent newtypes have at most one non-ZST field which needs to be checked..
1108                match self.check_field_type_for_ffi(acc, field, args) {
1109                    FfiUnsafe { ty, .. } if ty.is_unit() => (),
1110                    r => return r,
1111                }
1112
1113                false
1114            } else {
1115                // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
1116                // `PhantomData`).
1117                true
1118            }
1119        } else {
1120            false
1121        };
1122
1123        // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
1124        let mut all_phantom = !variant.fields.is_empty();
1125        for field in &variant.fields {
1126            all_phantom &= match self.check_field_type_for_ffi(acc, field, args) {
1127                FfiSafe => false,
1128                // `()` fields are FFI-safe!
1129                FfiUnsafe { ty, .. } if ty.is_unit() => false,
1130                FfiPhantom(..) => true,
1131                r @ FfiUnsafe { .. } => return r,
1132            }
1133        }
1134
1135        if all_phantom {
1136            FfiPhantom(ty)
1137        } else if transparent_with_all_zst_fields {
1138            FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
1139        } else {
1140            FfiSafe
1141        }
1142    }
1143
1144    /// Checks if the given type is "ffi-safe" (has a stable, well-defined
1145    /// representation which can be exported to C code).
1146    fn check_type_for_ffi(
1147        &self,
1148        acc: &mut CTypesVisitorState<'tcx>,
1149        ty: Ty<'tcx>,
1150    ) -> FfiResult<'tcx> {
1151        use FfiResult::*;
1152
1153        let tcx = self.cx.tcx;
1154
1155        // Protect against infinite recursion, for example
1156        // `struct S(*mut S);`.
1157        // FIXME: A recursion limit is necessary as well, for irregular
1158        // recursive types.
1159        if !acc.cache.insert(ty) {
1160            return FfiSafe;
1161        }
1162
1163        match *ty.kind() {
1164            ty::Adt(def, args) => {
1165                if let Some(boxed) = ty.boxed_ty()
1166                    && matches!(self.mode, CItemKind::Definition)
1167                {
1168                    if boxed.is_sized(tcx, self.cx.typing_env()) {
1169                        return FfiSafe;
1170                    } else {
1171                        return FfiUnsafe {
1172                            ty,
1173                            reason: fluent::lint_improper_ctypes_box,
1174                            help: None,
1175                        };
1176                    }
1177                }
1178                if def.is_phantom_data() {
1179                    return FfiPhantom(ty);
1180                }
1181                match def.adt_kind() {
1182                    AdtKind::Struct | AdtKind::Union => {
1183                        if let Some(sym::cstring_type | sym::cstr_type) =
1184                            tcx.get_diagnostic_name(def.did())
1185                            && !acc.base_ty.is_mutable_ptr()
1186                        {
1187                            return FfiUnsafe {
1188                                ty,
1189                                reason: fluent::lint_improper_ctypes_cstr_reason,
1190                                help: Some(fluent::lint_improper_ctypes_cstr_help),
1191                            };
1192                        }
1193
1194                        if !def.repr().c() && !def.repr().transparent() {
1195                            return FfiUnsafe {
1196                                ty,
1197                                reason: if def.is_struct() {
1198                                    fluent::lint_improper_ctypes_struct_layout_reason
1199                                } else {
1200                                    fluent::lint_improper_ctypes_union_layout_reason
1201                                },
1202                                help: if def.is_struct() {
1203                                    Some(fluent::lint_improper_ctypes_struct_layout_help)
1204                                } else {
1205                                    Some(fluent::lint_improper_ctypes_union_layout_help)
1206                                },
1207                            };
1208                        }
1209
1210                        let is_non_exhaustive =
1211                            def.non_enum_variant().is_field_list_non_exhaustive();
1212                        if is_non_exhaustive && !def.did().is_local() {
1213                            return FfiUnsafe {
1214                                ty,
1215                                reason: if def.is_struct() {
1216                                    fluent::lint_improper_ctypes_struct_non_exhaustive
1217                                } else {
1218                                    fluent::lint_improper_ctypes_union_non_exhaustive
1219                                },
1220                                help: None,
1221                            };
1222                        }
1223
1224                        if def.non_enum_variant().fields.is_empty() {
1225                            return FfiUnsafe {
1226                                ty,
1227                                reason: if def.is_struct() {
1228                                    fluent::lint_improper_ctypes_struct_fieldless_reason
1229                                } else {
1230                                    fluent::lint_improper_ctypes_union_fieldless_reason
1231                                },
1232                                help: if def.is_struct() {
1233                                    Some(fluent::lint_improper_ctypes_struct_fieldless_help)
1234                                } else {
1235                                    Some(fluent::lint_improper_ctypes_union_fieldless_help)
1236                                },
1237                            };
1238                        }
1239
1240                        self.check_variant_for_ffi(acc, ty, def, def.non_enum_variant(), args)
1241                    }
1242                    AdtKind::Enum => {
1243                        if def.variants().is_empty() {
1244                            // Empty enums are okay... although sort of useless.
1245                            return FfiSafe;
1246                        }
1247                        // Check for a repr() attribute to specify the size of the
1248                        // discriminant.
1249                        if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none()
1250                        {
1251                            // Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>`
1252                            if let Some(ty) =
1253                                repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty, self.mode)
1254                            {
1255                                return self.check_type_for_ffi(acc, ty);
1256                            }
1257
1258                            return FfiUnsafe {
1259                                ty,
1260                                reason: fluent::lint_improper_ctypes_enum_repr_reason,
1261                                help: Some(fluent::lint_improper_ctypes_enum_repr_help),
1262                            };
1263                        }
1264
1265                        use improper_ctypes::{
1266                            check_non_exhaustive_variant, non_local_and_non_exhaustive,
1267                        };
1268
1269                        let non_local_def = non_local_and_non_exhaustive(def);
1270                        // Check the contained variants.
1271                        let ret = def.variants().iter().try_for_each(|variant| {
1272                            check_non_exhaustive_variant(non_local_def, variant)
1273                                .map_break(|reason| FfiUnsafe { ty, reason, help: None })?;
1274
1275                            match self.check_variant_for_ffi(acc, ty, def, variant, args) {
1276                                FfiSafe => ControlFlow::Continue(()),
1277                                r => ControlFlow::Break(r),
1278                            }
1279                        });
1280                        if let ControlFlow::Break(result) = ret {
1281                            return result;
1282                        }
1283
1284                        FfiSafe
1285                    }
1286                }
1287            }
1288
1289            ty::Char => FfiUnsafe {
1290                ty,
1291                reason: fluent::lint_improper_ctypes_char_reason,
1292                help: Some(fluent::lint_improper_ctypes_char_help),
1293            },
1294
1295            // It's just extra invariants on the type that you need to uphold,
1296            // but only the base type is relevant for being representable in FFI.
1297            ty::Pat(base, ..) => self.check_type_for_ffi(acc, base),
1298
1299            ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => {
1300                FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None }
1301            }
1302
1303            // Primitive types with a stable representation.
1304            ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe,
1305
1306            ty::Slice(_) => FfiUnsafe {
1307                ty,
1308                reason: fluent::lint_improper_ctypes_slice_reason,
1309                help: Some(fluent::lint_improper_ctypes_slice_help),
1310            },
1311
1312            ty::Dynamic(..) => {
1313                FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None }
1314            }
1315
1316            ty::Str => FfiUnsafe {
1317                ty,
1318                reason: fluent::lint_improper_ctypes_str_reason,
1319                help: Some(fluent::lint_improper_ctypes_str_help),
1320            },
1321
1322            ty::Tuple(..) => FfiUnsafe {
1323                ty,
1324                reason: fluent::lint_improper_ctypes_tuple_reason,
1325                help: Some(fluent::lint_improper_ctypes_tuple_help),
1326            },
1327
1328            ty::RawPtr(ty, _) | ty::Ref(_, ty, _)
1329                if {
1330                    matches!(self.mode, CItemKind::Definition)
1331                        && ty.is_sized(self.cx.tcx, self.cx.typing_env())
1332                } =>
1333            {
1334                FfiSafe
1335            }
1336
1337            ty::RawPtr(ty, _)
1338                if match ty.kind() {
1339                    ty::Tuple(tuple) => tuple.is_empty(),
1340                    _ => false,
1341                } =>
1342            {
1343                FfiSafe
1344            }
1345
1346            ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(acc, ty),
1347
1348            ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty),
1349
1350            ty::FnPtr(sig_tys, hdr) => {
1351                let sig = sig_tys.with(hdr);
1352                if self.is_internal_abi(sig.abi()) {
1353                    return FfiUnsafe {
1354                        ty,
1355                        reason: fluent::lint_improper_ctypes_fnptr_reason,
1356                        help: Some(fluent::lint_improper_ctypes_fnptr_help),
1357                    };
1358                }
1359
1360                let sig = tcx.instantiate_bound_regions_with_erased(sig);
1361                for arg in sig.inputs() {
1362                    match self.check_type_for_ffi(acc, *arg) {
1363                        FfiSafe => {}
1364                        r => return r,
1365                    }
1366                }
1367
1368                let ret_ty = sig.output();
1369                if ret_ty.is_unit() {
1370                    return FfiSafe;
1371                }
1372
1373                self.check_type_for_ffi(acc, ret_ty)
1374            }
1375
1376            ty::Foreign(..) => FfiSafe,
1377
1378            // While opaque types are checked for earlier, if a projection in a struct field
1379            // normalizes to an opaque type, then it will reach this branch.
1380            ty::Alias(ty::Opaque, ..) => {
1381                FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None }
1382            }
1383
1384            // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
1385            //  so they are currently ignored for the purposes of this lint.
1386            ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
1387                if matches!(self.mode, CItemKind::Definition) =>
1388            {
1389                FfiSafe
1390            }
1391
1392            ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
1393
1394            ty::Param(..)
1395            | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
1396            | ty::Infer(..)
1397            | ty::Bound(..)
1398            | ty::Error(_)
1399            | ty::Closure(..)
1400            | ty::CoroutineClosure(..)
1401            | ty::Coroutine(..)
1402            | ty::CoroutineWitness(..)
1403            | ty::Placeholder(..)
1404            | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
1405        }
1406    }
1407
1408    fn emit_ffi_unsafe_type_lint(
1409        &mut self,
1410        ty: Ty<'tcx>,
1411        sp: Span,
1412        note: DiagMessage,
1413        help: Option<DiagMessage>,
1414    ) {
1415        let lint = match self.mode {
1416            CItemKind::Declaration => IMPROPER_CTYPES,
1417            CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
1418        };
1419        let desc = match self.mode {
1420            CItemKind::Declaration => "block",
1421            CItemKind::Definition => "fn",
1422        };
1423        let span_note = if let ty::Adt(def, _) = ty.kind()
1424            && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did())
1425        {
1426            Some(sp)
1427        } else {
1428            None
1429        };
1430        self.cx.emit_span_lint(
1431            lint,
1432            sp,
1433            ImproperCTypes { ty, desc, label: sp, help, note, span_note },
1434        );
1435    }
1436
1437    fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
1438        struct ProhibitOpaqueTypes;
1439        impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes {
1440            type Result = ControlFlow<Ty<'tcx>>;
1441
1442            fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
1443                if !ty.has_opaque_types() {
1444                    return ControlFlow::Continue(());
1445                }
1446
1447                if let ty::Alias(ty::Opaque, ..) = ty.kind() {
1448                    ControlFlow::Break(ty)
1449                } else {
1450                    ty.super_visit_with(self)
1451                }
1452            }
1453        }
1454
1455        if let Some(ty) = self
1456            .cx
1457            .tcx
1458            .try_normalize_erasing_regions(self.cx.typing_env(), ty)
1459            .unwrap_or(ty)
1460            .visit_with(&mut ProhibitOpaqueTypes)
1461            .break_value()
1462        {
1463            self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None);
1464            true
1465        } else {
1466            false
1467        }
1468    }
1469
1470    fn check_type_for_ffi_and_report_errors(
1471        &mut self,
1472        sp: Span,
1473        ty: Ty<'tcx>,
1474        is_static: bool,
1475        is_return_type: bool,
1476    ) {
1477        if self.check_for_opaque_ty(sp, ty) {
1478            // We've already emitted an error due to an opaque type.
1479            return;
1480        }
1481
1482        let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty);
1483
1484        // C doesn't really support passing arrays by value - the only way to pass an array by value
1485        // is through a struct. So, first test that the top level isn't an array, and then
1486        // recursively check the types inside.
1487        if !is_static && self.check_for_array_ty(sp, ty) {
1488            return;
1489        }
1490
1491        // Don't report FFI errors for unit return types. This check exists here, and not in
1492        // the caller (where it would make more sense) so that normalization has definitely
1493        // happened.
1494        if is_return_type && ty.is_unit() {
1495            return;
1496        }
1497
1498        let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty };
1499        match self.check_type_for_ffi(&mut acc, ty) {
1500            FfiResult::FfiSafe => {}
1501            FfiResult::FfiPhantom(ty) => {
1502                self.emit_ffi_unsafe_type_lint(
1503                    ty,
1504                    sp,
1505                    fluent::lint_improper_ctypes_only_phantomdata,
1506                    None,
1507                );
1508            }
1509            FfiResult::FfiUnsafe { ty, reason, help } => {
1510                self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
1511            }
1512        }
1513    }
1514
1515    /// Check if a function's argument types and result type are "ffi-safe".
1516    ///
1517    /// For a external ABI function, argument types and the result type are walked to find fn-ptr
1518    /// types that have external ABIs, as these still need checked.
1519    fn check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
1520        let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity();
1521        let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig);
1522
1523        for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1524            for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) {
1525                self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, false);
1526            }
1527        }
1528
1529        if let hir::FnRetTy::Return(ret_hir) = decl.output {
1530            for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(ret_hir, sig.output()) {
1531                self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, true);
1532            }
1533        }
1534    }
1535
1536    /// Check if a function's argument types and result type are "ffi-safe".
1537    fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
1538        let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity();
1539        let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig);
1540
1541        for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1542            self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false);
1543        }
1544
1545        if let hir::FnRetTy::Return(ret_hir) = decl.output {
1546            self.check_type_for_ffi_and_report_errors(ret_hir.span, sig.output(), false, true);
1547        }
1548    }
1549
1550    fn check_foreign_static(&mut self, id: hir::OwnerId, span: Span) {
1551        let ty = self.cx.tcx.type_of(id).instantiate_identity();
1552        self.check_type_for_ffi_and_report_errors(span, ty, true, false);
1553    }
1554
1555    fn is_internal_abi(&self, abi: ExternAbi) -> bool {
1556        matches!(
1557            abi,
1558            ExternAbi::Rust | ExternAbi::RustCall | ExternAbi::RustCold | ExternAbi::RustIntrinsic
1559        )
1560    }
1561
1562    /// Find any fn-ptr types with external ABIs in `ty`.
1563    ///
1564    /// For example, `Option<extern "C" fn()>` returns `extern "C" fn()`
1565    fn find_fn_ptr_ty_with_external_abi(
1566        &self,
1567        hir_ty: &hir::Ty<'tcx>,
1568        ty: Ty<'tcx>,
1569    ) -> Vec<(Ty<'tcx>, Span)> {
1570        struct FnPtrFinder<'a, 'b, 'tcx> {
1571            visitor: &'a ImproperCTypesVisitor<'b, 'tcx>,
1572            spans: Vec<Span>,
1573            tys: Vec<Ty<'tcx>>,
1574        }
1575
1576        impl<'a, 'b, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'a, 'b, 'tcx> {
1577            fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) {
1578                debug!(?ty);
1579                if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind
1580                    && !self.visitor.is_internal_abi(*abi)
1581                {
1582                    self.spans.push(ty.span);
1583                }
1584
1585                hir::intravisit::walk_ty(self, ty)
1586            }
1587        }
1588
1589        impl<'a, 'b, 'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'a, 'b, 'tcx> {
1590            type Result = ControlFlow<Ty<'tcx>>;
1591
1592            fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
1593                if let ty::FnPtr(_, hdr) = ty.kind()
1594                    && !self.visitor.is_internal_abi(hdr.abi)
1595                {
1596                    self.tys.push(ty);
1597                }
1598
1599                ty.super_visit_with(self)
1600            }
1601        }
1602
1603        let mut visitor = FnPtrFinder { visitor: self, spans: Vec::new(), tys: Vec::new() };
1604        ty.visit_with(&mut visitor);
1605        visitor.visit_ty_unambig(hir_ty);
1606
1607        iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect()
1608    }
1609}
1610
1611impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations {
1612    fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
1613        let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration };
1614        let abi = cx.tcx.hir().get_foreign_abi(it.hir_id());
1615
1616        match it.kind {
1617            hir::ForeignItemKind::Fn(sig, _, _) => {
1618                if vis.is_internal_abi(abi) {
1619                    vis.check_fn(it.owner_id.def_id, sig.decl)
1620                } else {
1621                    vis.check_foreign_fn(it.owner_id.def_id, sig.decl);
1622                }
1623            }
1624            hir::ForeignItemKind::Static(ty, _, _) if !vis.is_internal_abi(abi) => {
1625                vis.check_foreign_static(it.owner_id, ty.span);
1626            }
1627            hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
1628        }
1629    }
1630}
1631
1632impl ImproperCTypesDefinitions {
1633    fn check_ty_maybe_containing_foreign_fnptr<'tcx>(
1634        &mut self,
1635        cx: &LateContext<'tcx>,
1636        hir_ty: &'tcx hir::Ty<'_>,
1637        ty: Ty<'tcx>,
1638    ) {
1639        let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
1640        for (fn_ptr_ty, span) in vis.find_fn_ptr_ty_with_external_abi(hir_ty, ty) {
1641            vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false);
1642        }
1643    }
1644
1645    fn check_arg_for_power_alignment<'tcx>(
1646        &mut self,
1647        cx: &LateContext<'tcx>,
1648        ty: Ty<'tcx>,
1649    ) -> bool {
1650        // Structs (under repr(C)) follow the power alignment rule if:
1651        //   - the first field of the struct is a floating-point type that
1652        //     is greater than 4-bytes, or
1653        //   - the first field of the struct is an aggregate whose
1654        //     recursively first field is a floating-point type greater than
1655        //     4 bytes.
1656        if cx.tcx.sess.target.os != "aix" {
1657            return false;
1658        }
1659        if ty.is_floating_point() && ty.primitive_size(cx.tcx).bytes() > 4 {
1660            return true;
1661        } else if let Adt(adt_def, _) = ty.kind()
1662            && adt_def.is_struct()
1663        {
1664            let struct_variant = adt_def.variant(VariantIdx::ZERO);
1665            // Within a nested struct, all fields are examined to correctly
1666            // report if any fields after the nested struct within the
1667            // original struct are misaligned.
1668            for struct_field in &struct_variant.fields {
1669                let field_ty = cx.tcx.type_of(struct_field.did).instantiate_identity();
1670                if self.check_arg_for_power_alignment(cx, field_ty) {
1671                    return true;
1672                }
1673            }
1674        }
1675        return false;
1676    }
1677
1678    fn check_struct_for_power_alignment<'tcx>(
1679        &mut self,
1680        cx: &LateContext<'tcx>,
1681        item: &'tcx hir::Item<'tcx>,
1682    ) {
1683        let adt_def = cx.tcx.adt_def(item.owner_id.to_def_id());
1684        if adt_def.repr().c()
1685            && !adt_def.repr().packed()
1686            && cx.tcx.sess.target.os == "aix"
1687            && !adt_def.all_fields().next().is_none()
1688        {
1689            let struct_variant_data = item.expect_struct().0;
1690            for (index, ..) in struct_variant_data.fields().iter().enumerate() {
1691                // Struct fields (after the first field) are checked for the
1692                // power alignment rule, as fields after the first are likely
1693                // to be the fields that are misaligned.
1694                if index != 0 {
1695                    let first_field_def = struct_variant_data.fields()[index];
1696                    let def_id = first_field_def.def_id;
1697                    let ty = cx.tcx.type_of(def_id).instantiate_identity();
1698                    if self.check_arg_for_power_alignment(cx, ty) {
1699                        cx.emit_span_lint(
1700                            USES_POWER_ALIGNMENT,
1701                            first_field_def.span,
1702                            UsesPowerAlignment,
1703                        );
1704                    }
1705                }
1706            }
1707        }
1708    }
1709}
1710
1711/// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in
1712/// `extern "C" { }` blocks):
1713///
1714/// - `extern "<abi>" fn` definitions are checked in the same way as the
1715///   `ImproperCtypesDeclarations` visitor checks functions if `<abi>` is external (e.g. "C").
1716/// - All other items which contain types (e.g. other functions, struct definitions, etc) are
1717///   checked for extern fn-ptrs with external ABIs.
1718impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
1719    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
1720        match item.kind {
1721            hir::ItemKind::Static(ty, ..)
1722            | hir::ItemKind::Const(ty, ..)
1723            | hir::ItemKind::TyAlias(ty, ..) => {
1724                self.check_ty_maybe_containing_foreign_fnptr(
1725                    cx,
1726                    ty,
1727                    cx.tcx.type_of(item.owner_id).instantiate_identity(),
1728                );
1729            }
1730            // See `check_fn`..
1731            hir::ItemKind::Fn { .. } => {}
1732            // Structs are checked based on if they follow the power alignment
1733            // rule (under repr(C)).
1734            hir::ItemKind::Struct(..) => {
1735                self.check_struct_for_power_alignment(cx, item);
1736            }
1737            // See `check_field_def`..
1738            hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {}
1739            // Doesn't define something that can contain a external type to be checked.
1740            hir::ItemKind::Impl(..)
1741            | hir::ItemKind::TraitAlias(..)
1742            | hir::ItemKind::Trait(..)
1743            | hir::ItemKind::GlobalAsm(..)
1744            | hir::ItemKind::ForeignMod { .. }
1745            | hir::ItemKind::Mod(..)
1746            | hir::ItemKind::Macro(..)
1747            | hir::ItemKind::Use(..)
1748            | hir::ItemKind::ExternCrate(..) => {}
1749        }
1750    }
1751
1752    fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
1753        self.check_ty_maybe_containing_foreign_fnptr(
1754            cx,
1755            field.ty,
1756            cx.tcx.type_of(field.def_id).instantiate_identity(),
1757        );
1758    }
1759
1760    fn check_fn(
1761        &mut self,
1762        cx: &LateContext<'tcx>,
1763        kind: hir::intravisit::FnKind<'tcx>,
1764        decl: &'tcx hir::FnDecl<'_>,
1765        _: &'tcx hir::Body<'_>,
1766        _: Span,
1767        id: LocalDefId,
1768    ) {
1769        use hir::intravisit::FnKind;
1770
1771        let abi = match kind {
1772            FnKind::ItemFn(_, _, header, ..) => header.abi,
1773            FnKind::Method(_, sig, ..) => sig.header.abi,
1774            _ => return,
1775        };
1776
1777        let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
1778        if vis.is_internal_abi(abi) {
1779            vis.check_fn(id, decl);
1780        } else {
1781            vis.check_foreign_fn(id, decl);
1782        }
1783    }
1784}
1785
1786declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]);
1787
1788impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
1789    fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
1790        if let hir::ItemKind::Enum(ref enum_definition, _) = it.kind {
1791            let t = cx.tcx.type_of(it.owner_id).instantiate_identity();
1792            let ty = cx.tcx.erase_regions(t);
1793            let Ok(layout) = cx.layout_of(ty) else { return };
1794            let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, ref variants, .. } =
1795                &layout.variants
1796            else {
1797                return;
1798            };
1799
1800            let tag_size = tag.size(&cx.tcx).bytes();
1801
1802            debug!(
1803                "enum `{}` is {} bytes large with layout:\n{:#?}",
1804                t,
1805                layout.size.bytes(),
1806                layout
1807            );
1808
1809            let (largest, slargest, largest_index) = iter::zip(enum_definition.variants, variants)
1810                .map(|(variant, variant_layout)| {
1811                    // Subtract the size of the enum tag.
1812                    let bytes = variant_layout.size.bytes().saturating_sub(tag_size);
1813
1814                    debug!("- variant `{}` is {} bytes large", variant.ident, bytes);
1815                    bytes
1816                })
1817                .enumerate()
1818                .fold((0, 0, 0), |(l, s, li), (idx, size)| {
1819                    if size > l {
1820                        (size, l, idx)
1821                    } else if size > s {
1822                        (l, size, li)
1823                    } else {
1824                        (l, s, li)
1825                    }
1826                });
1827
1828            // We only warn if the largest variant is at least thrice as large as
1829            // the second-largest.
1830            if largest > slargest * 3 && slargest > 0 {
1831                cx.emit_span_lint(
1832                    VARIANT_SIZE_DIFFERENCES,
1833                    enum_definition.variants[largest_index].span,
1834                    VariantSizeDifferencesDiag { largest },
1835                );
1836            }
1837        }
1838    }
1839}
1840
1841declare_lint! {
1842    /// The `invalid_atomic_ordering` lint detects passing an `Ordering`
1843    /// to an atomic operation that does not support that ordering.
1844    ///
1845    /// ### Example
1846    ///
1847    /// ```rust,compile_fail
1848    /// # use core::sync::atomic::{AtomicU8, Ordering};
1849    /// let atom = AtomicU8::new(0);
1850    /// let value = atom.load(Ordering::Release);
1851    /// # let _ = value;
1852    /// ```
1853    ///
1854    /// {{produces}}
1855    ///
1856    /// ### Explanation
1857    ///
1858    /// Some atomic operations are only supported for a subset of the
1859    /// `atomic::Ordering` variants. Passing an unsupported variant will cause
1860    /// an unconditional panic at runtime, which is detected by this lint.
1861    ///
1862    /// This lint will trigger in the following cases: (where `AtomicType` is an
1863    /// atomic type from `core::sync::atomic`, such as `AtomicBool`,
1864    /// `AtomicPtr`, `AtomicUsize`, or any of the other integer atomics).
1865    ///
1866    /// - Passing `Ordering::Acquire` or `Ordering::AcqRel` to
1867    ///   `AtomicType::store`.
1868    ///
1869    /// - Passing `Ordering::Release` or `Ordering::AcqRel` to
1870    ///   `AtomicType::load`.
1871    ///
1872    /// - Passing `Ordering::Relaxed` to `core::sync::atomic::fence` or
1873    ///   `core::sync::atomic::compiler_fence`.
1874    ///
1875    /// - Passing `Ordering::Release` or `Ordering::AcqRel` as the failure
1876    ///   ordering for any of `AtomicType::compare_exchange`,
1877    ///   `AtomicType::compare_exchange_weak`, or `AtomicType::fetch_update`.
1878    INVALID_ATOMIC_ORDERING,
1879    Deny,
1880    "usage of invalid atomic ordering in atomic operations and memory fences"
1881}
1882
1883declare_lint_pass!(InvalidAtomicOrdering => [INVALID_ATOMIC_ORDERING]);
1884
1885impl InvalidAtomicOrdering {
1886    fn inherent_atomic_method_call<'hir>(
1887        cx: &LateContext<'_>,
1888        expr: &Expr<'hir>,
1889        recognized_names: &[Symbol], // used for fast path calculation
1890    ) -> Option<(Symbol, &'hir [Expr<'hir>])> {
1891        const ATOMIC_TYPES: &[Symbol] = &[
1892            sym::AtomicBool,
1893            sym::AtomicPtr,
1894            sym::AtomicUsize,
1895            sym::AtomicU8,
1896            sym::AtomicU16,
1897            sym::AtomicU32,
1898            sym::AtomicU64,
1899            sym::AtomicU128,
1900            sym::AtomicIsize,
1901            sym::AtomicI8,
1902            sym::AtomicI16,
1903            sym::AtomicI32,
1904            sym::AtomicI64,
1905            sym::AtomicI128,
1906        ];
1907        if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind
1908            && recognized_names.contains(&method_path.ident.name)
1909            && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
1910            && let Some(impl_did) = cx.tcx.impl_of_method(m_def_id)
1911            && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
1912            // skip extension traits, only lint functions from the standard library
1913            && cx.tcx.trait_id_of_impl(impl_did).is_none()
1914            && let parent = cx.tcx.parent(adt.did())
1915            && cx.tcx.is_diagnostic_item(sym::atomic_mod, parent)
1916            && ATOMIC_TYPES.contains(&cx.tcx.item_name(adt.did()))
1917        {
1918            return Some((method_path.ident.name, args));
1919        }
1920        None
1921    }
1922
1923    fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol> {
1924        let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None };
1925        let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?;
1926        let tcx = cx.tcx;
1927        let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering);
1928        let name = tcx.item_name(did);
1929        let parent = tcx.parent(did);
1930        [sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find(
1931            |&ordering| {
1932                name == ordering
1933                    && (Some(parent) == atomic_ordering
1934                            // needed in case this is a ctor, not a variant
1935                            || tcx.opt_parent(parent) == atomic_ordering)
1936            },
1937        )
1938    }
1939
1940    fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
1941        if let Some((method, args)) =
1942            Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
1943            && let Some((ordering_arg, invalid_ordering)) = match method {
1944                sym::load => Some((&args[0], sym::Release)),
1945                sym::store => Some((&args[1], sym::Acquire)),
1946                _ => None,
1947            }
1948            && let Some(ordering) = Self::match_ordering(cx, ordering_arg)
1949            && (ordering == invalid_ordering || ordering == sym::AcqRel)
1950        {
1951            if method == sym::load {
1952                cx.emit_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad);
1953            } else {
1954                cx.emit_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore);
1955            };
1956        }
1957    }
1958
1959    fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
1960        if let ExprKind::Call(func, args) = expr.kind
1961            && let ExprKind::Path(ref func_qpath) = func.kind
1962            && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
1963            && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
1964            && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
1965        {
1966            cx.emit_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence);
1967        }
1968    }
1969
1970    fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
1971        let Some((method, args)) = Self::inherent_atomic_method_call(
1972            cx,
1973            expr,
1974            &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak],
1975        ) else {
1976            return;
1977        };
1978
1979        let fail_order_arg = match method {
1980            sym::fetch_update => &args[1],
1981            sym::compare_exchange | sym::compare_exchange_weak => &args[3],
1982            _ => return,
1983        };
1984
1985        let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
1986
1987        if matches!(fail_ordering, sym::Release | sym::AcqRel) {
1988            cx.emit_span_lint(
1989                INVALID_ATOMIC_ORDERING,
1990                fail_order_arg.span,
1991                InvalidAtomicOrderingDiag { method, fail_order_arg_span: fail_order_arg.span },
1992            );
1993        }
1994    }
1995}
1996
1997impl<'tcx> LateLintPass<'tcx> for InvalidAtomicOrdering {
1998    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1999        Self::check_atomic_load_store(cx, expr);
2000        Self::check_memory_fence(cx, expr);
2001        Self::check_atomic_compare_exchange(cx, expr);
2002    }
2003}