rustc_lint/
types.rs

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