rustc_lint/
types.rs

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