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