Skip to main content

rustc_lint/
types.rs

1use std::iter;
2
3use rustc_abi::{BackendRepr, TagEncoding, Variants, WrappingRange};
4use rustc_ast as ast;
5use rustc_attr_ir::find_attr;
6use rustc_attr_ir::lang_items::LangItem;
7use rustc_hir as hir;
8use rustc_hir::{Expr, ExprKind, HirId};
9use rustc_lint_defs::{declare_lint, declare_lint_pass, impl_lint_pass};
10use rustc_middle::ty::consts::ConstExt;
11use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton};
12use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Unnormalized};
13use rustc_span::{DUMMY_SP, Span, Symbol, bug, sym};
14use tracing::debug;
15
16mod improper_ctypes; // these files do the implementation for ImproperCTypesDefinitions,ImproperCTypesDeclarations
17pub(crate) use improper_ctypes::ImproperCTypesLint;
18
19use crate::diagnostics::{
20    AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
21    AmbiguousWidePointerComparisonsAddrSuggestion, AmbiguousWidePointerComparisonsCastSuggestion,
22    AmbiguousWidePointerComparisonsExpectSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
23    AtomicOrderingStore, InvalidAtomicOrderingDiag, InvalidNanComparisons,
24    InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
25    UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons,
26    VariantSizeDifferencesDiag,
27};
28use crate::utils::std_or_core;
29use crate::{LateContext, LateLintPass, LintContext};
30
31mod literal;
32use literal::{int_ty_range, lint_literal, uint_ty_range};
33
34#[doc = r" The `unused_comparisons` lint detects comparisons made useless by"]
#[doc = r" limits of the types involved."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" fn foo(x: u8) {"]
#[doc = r"     x >= 0;"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" A useless comparison may indicate a mistake, and should be fixed or"]
#[doc = r" removed."]
static UNUSED_COMPARISONS: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "UNUSED_COMPARISONS",
            default_level: ::rustc_lint_defs::Warn,
            desc: "comparisons made useless by limits of the types involved",
            is_externally_loaded: false,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
35    /// The `unused_comparisons` lint detects comparisons made useless by
36    /// limits of the types involved.
37    ///
38    /// ### Example
39    ///
40    /// ```rust
41    /// fn foo(x: u8) {
42    ///     x >= 0;
43    /// }
44    /// ```
45    ///
46    /// {{produces}}
47    ///
48    /// ### Explanation
49    ///
50    /// A useless comparison may indicate a mistake, and should be fixed or
51    /// removed.
52    UNUSED_COMPARISONS,
53    Warn,
54    "comparisons made useless by limits of the types involved"
55}
56
57#[doc =
r" The `overflowing_literals` lint detects literals out of range for their type."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" let x: u8 = 1000;"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" It is usually a mistake to use a literal that overflows its type"]
#[doc = r" Change either the literal or its type such that the literal is"]
#[doc = r" within the range of its type."]
static OVERFLOWING_LITERALS: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "OVERFLOWING_LITERALS",
            default_level: ::rustc_lint_defs::Deny,
            desc: "literal out of range for its type",
            is_externally_loaded: false,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
58    /// The `overflowing_literals` lint detects literals out of range for their type.
59    ///
60    /// ### Example
61    ///
62    /// ```rust,compile_fail
63    /// let x: u8 = 1000;
64    /// ```
65    ///
66    /// {{produces}}
67    ///
68    /// ### Explanation
69    ///
70    /// It is usually a mistake to use a literal that overflows its type
71    /// Change either the literal or its type such that the literal is
72    /// within the range of its type.
73    OVERFLOWING_LITERALS,
74    Deny,
75    "literal out of range for its type"
76}
77
78#[doc =
r" The `variant_size_differences` lint detects enums with widely varying"]
#[doc = r" variant sizes."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" #![deny(variant_size_differences)]"]
#[doc = r" enum En {"]
#[doc = r"     V0(u8),"]
#[doc = r"     VBig([u8; 1024]),"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" It can be a mistake to add a variant to an enum that is much larger"]
#[doc =
r" than the other variants, bloating the overall size required for all"]
#[doc = r" variants. This can impact performance and memory usage. This is"]
#[doc = r" triggered if one variant is more than 3 times larger than the"]
#[doc = r" second-largest variant."]
#[doc = r""]
#[doc =
r" Consider placing the large variant's contents on the heap (for example"]
#[doc = r" via [`Box`]) to keep the overall size of the enum itself down."]
#[doc = r""]
#[doc =
r#" This lint is "allow" by default because it can be noisy, and may not be"#]
#[doc = r" an actual problem. Decisions about this should be guided with"]
#[doc = r" profiling and benchmarking."]
#[doc = r""]
#[doc = r" [`Box`]: https://doc.rust-lang.org/std/boxed/index.html"]
static VARIANT_SIZE_DIFFERENCES: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "VARIANT_SIZE_DIFFERENCES",
            default_level: ::rustc_lint_defs::Allow,
            desc: "detects enums with widely varying variant sizes",
            is_externally_loaded: false,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
79    /// The `variant_size_differences` lint detects enums with widely varying
80    /// variant sizes.
81    ///
82    /// ### Example
83    ///
84    /// ```rust,compile_fail
85    /// #![deny(variant_size_differences)]
86    /// enum En {
87    ///     V0(u8),
88    ///     VBig([u8; 1024]),
89    /// }
90    /// ```
91    ///
92    /// {{produces}}
93    ///
94    /// ### Explanation
95    ///
96    /// It can be a mistake to add a variant to an enum that is much larger
97    /// than the other variants, bloating the overall size required for all
98    /// variants. This can impact performance and memory usage. This is
99    /// triggered if one variant is more than 3 times larger than the
100    /// second-largest variant.
101    ///
102    /// Consider placing the large variant's contents on the heap (for example
103    /// via [`Box`]) to keep the overall size of the enum itself down.
104    ///
105    /// This lint is "allow" by default because it can be noisy, and may not be
106    /// an actual problem. Decisions about this should be guided with
107    /// profiling and benchmarking.
108    ///
109    /// [`Box`]: https://doc.rust-lang.org/std/boxed/index.html
110    VARIANT_SIZE_DIFFERENCES,
111    Allow,
112    "detects enums with widely varying variant sizes"
113}
114
115#[doc =
r" The `invalid_nan_comparisons` lint checks comparison with `f32::NAN` or `f64::NAN`"]
#[doc = r" as one of the operand."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" let a = 2.3f32;"]
#[doc = r" if a == f32::NAN {}"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" NaN does not compare meaningfully to anything – not"]
#[doc = r" even itself – so those comparisons are always false."]
static INVALID_NAN_COMPARISONS: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "INVALID_NAN_COMPARISONS",
            default_level: ::rustc_lint_defs::Warn,
            desc: "detects invalid floating point NaN comparisons",
            is_externally_loaded: false,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
116    /// The `invalid_nan_comparisons` lint checks comparison with `f32::NAN` or `f64::NAN`
117    /// as one of the operand.
118    ///
119    /// ### Example
120    ///
121    /// ```rust
122    /// let a = 2.3f32;
123    /// if a == f32::NAN {}
124    /// ```
125    ///
126    /// {{produces}}
127    ///
128    /// ### Explanation
129    ///
130    /// NaN does not compare meaningfully to anything – not
131    /// even itself – so those comparisons are always false.
132    INVALID_NAN_COMPARISONS,
133    Warn,
134    "detects invalid floating point NaN comparisons"
135}
136
137#[doc = r" The `ambiguous_wide_pointer_comparisons` lint checks comparison"]
#[doc = r" of `*const/*mut ?Sized` as the operands."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" # struct A;"]
#[doc = r" # struct B;"]
#[doc = r""]
#[doc = r" # trait T {}"]
#[doc = r" # impl T for A {}"]
#[doc = r" # impl T for B {}"]
#[doc = r""]
#[doc = r" let ab = (A, B);"]
#[doc = r" let a = &ab.0 as *const dyn T;"]
#[doc = r" let b = &ab.1 as *const dyn T;"]
#[doc = r""]
#[doc = r" let _ = a == b;"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" The comparison includes metadata which may not be expected."]
static AMBIGUOUS_WIDE_POINTER_COMPARISONS: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "AMBIGUOUS_WIDE_POINTER_COMPARISONS",
            default_level: ::rustc_lint_defs::Warn,
            desc: "detects ambiguous wide pointer comparisons",
            is_externally_loaded: false,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
138    /// The `ambiguous_wide_pointer_comparisons` lint checks comparison
139    /// of `*const/*mut ?Sized` as the operands.
140    ///
141    /// ### Example
142    ///
143    /// ```rust
144    /// # struct A;
145    /// # struct B;
146    ///
147    /// # trait T {}
148    /// # impl T for A {}
149    /// # impl T for B {}
150    ///
151    /// let ab = (A, B);
152    /// let a = &ab.0 as *const dyn T;
153    /// let b = &ab.1 as *const dyn T;
154    ///
155    /// let _ = a == b;
156    /// ```
157    ///
158    /// {{produces}}
159    ///
160    /// ### Explanation
161    ///
162    /// The comparison includes metadata which may not be expected.
163    AMBIGUOUS_WIDE_POINTER_COMPARISONS,
164    Warn,
165    "detects ambiguous wide pointer comparisons"
166}
167
168#[doc =
r" The `unpredictable_function_pointer_comparisons` lint checks comparison"]
#[doc = r" of function pointer as the operands."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" fn a() {}"]
#[doc = r" fn b() {}"]
#[doc = r""]
#[doc = r" let f: fn() = a;"]
#[doc = r" let g: fn() = b;"]
#[doc = r""]
#[doc = r" let _ = f == g;"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" Function pointers comparisons do not produce meaningful result since"]
#[doc =
r" they are never guaranteed to be unique and could vary between different"]
#[doc =
r" code generation units. Furthermore, different functions could have the"]
#[doc = r" same address after being merged together."]
static UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS",
            default_level: ::rustc_lint_defs::Warn,
            desc: "detects unpredictable function pointer comparisons",
            is_externally_loaded: false,
            report_in_external_macro: true,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
169    /// The `unpredictable_function_pointer_comparisons` lint checks comparison
170    /// of function pointer as the operands.
171    ///
172    /// ### Example
173    ///
174    /// ```rust
175    /// fn a() {}
176    /// fn b() {}
177    ///
178    /// let f: fn() = a;
179    /// let g: fn() = b;
180    ///
181    /// let _ = f == g;
182    /// ```
183    ///
184    /// {{produces}}
185    ///
186    /// ### Explanation
187    ///
188    /// Function pointers comparisons do not produce meaningful result since
189    /// they are never guaranteed to be unique and could vary between different
190    /// code generation units. Furthermore, different functions could have the
191    /// same address after being merged together.
192    UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
193    Warn,
194    "detects unpredictable function pointer comparisons",
195    report_in_external_macro
196}
197
198#[derive(#[automatically_derived]
impl ::core::marker::Copy for TypeLimits { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for TypeLimits { }
#[automatically_derived]
impl ::core::clone::Clone for TypeLimits {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<Option<NegationInfo>>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::default::Default for TypeLimits {
    #[inline]
    fn default() -> Self {
        Self { last_visited_negation: ::core::default::Default::default() }
    }
}Default)]
199pub(crate) struct TypeLimits {
200    last_visited_negation: Option<NegationInfo>,
201}
202
203#[derive(#[automatically_derived]
impl ::core::marker::Copy for NegationInfo { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for NegationInfo { }
#[automatically_derived]
impl ::core::clone::Clone for NegationInfo {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<Span>;
        let _: ::core::clone::AssertParamIsClone<hir::HirId>;
        *self
    }
}Clone)]
204struct NegationInfo {
205    /// A negation expression (a `rustc_hir::ExprKind::Unary`)
206    negation_span: Span,
207    /// The operand of the negation expression.
208    negated_id: hir::HirId,
209}
210
211impl ::rustc_lint_defs::LintPass for TypeLimits {
    fn name(&self) -> &'static str { "TypeLimits" }
    fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [UNUSED_COMPARISONS, OVERFLOWING_LITERALS,
                        INVALID_NAN_COMPARISONS, AMBIGUOUS_WIDE_POINTER_COMPARISONS,
                        UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS]))
    }
}
impl TypeLimits {
    #[allow(unused)]
    pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [UNUSED_COMPARISONS, OVERFLOWING_LITERALS,
                        INVALID_NAN_COMPARISONS, AMBIGUOUS_WIDE_POINTER_COMPARISONS,
                        UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS]))
    }
}impl_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 { last_visited_negation: 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                #[allow(non_exhaustive_omitted_patterns)] match cx.tcx.get_diagnostic_name(def_id)
    {
    Some(sym::f16_nan | sym::f32_nan | sym::f64_nan | sym::f128_nan) => true,
    _ => false,
}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(#[automatically_derived]
impl ::core::fmt::Debug for ComparisonOp {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::BinOp(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "BinOp",
                    &__self_0),
            Self::Other => ::core::fmt::Formatter::write_str(f, "Other"),
        }
    }
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for ComparisonOp { }
#[automatically_derived]
impl ::core::cmp::PartialEq for ComparisonOp {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::BinOp(__self_0), Self::BinOp(__arg1_0)) =>
                    __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::marker::Copy for ComparisonOp { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ComparisonOp { }
#[automatically_derived]
impl ::core::clone::Clone for ComparisonOp {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<hir::BinOpKind>;
        *self
    }
}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_lang_item(def.did(), LangItem::NonNull) => {
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, #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
    ty::Dynamic(_, _) => true,
    _ => false,
}matches!(ty.kind(), ty::Dynamic(_, _))))
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(krate) = std_or_core(cx) else {
353        return cx.emit_span_lint(
354            AMBIGUOUS_WIDE_POINTER_COMPARISONS,
355            e.span,
356            AmbiguousWidePointerComparisons::Warn,
357        );
358    };
359
360    let (Some(l_span), Some(r_span)) =
361        (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
362    else {
363        return cx.emit_span_lint(
364            AMBIGUOUS_WIDE_POINTER_COMPARISONS,
365            e.span,
366            AmbiguousWidePointerComparisons::Spanless { krate },
367        );
368    };
369
370    let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
371    let is_eq_ne = #[allow(non_exhaustive_omitted_patterns)] match cmpop {
    ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne) => true,
    _ => false,
}matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne));
372    let is_dyn_comparison = l_inner_ty_is_dyn && r_inner_ty_is_dyn;
373    let via_method_call = #[allow(non_exhaustive_omitted_patterns)] match &e.kind {
    ExprKind::MethodCall(..) | ExprKind::Call(..) => true,
    _ => false,
}matches!(&e.kind, ExprKind::MethodCall(..) | ExprKind::Call(..));
374
375    let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
376    let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
377    let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
378
379    let deref_left = &*"*".repeat(l_ty_refs);
380    let deref_right = &*"*".repeat(r_ty_refs);
381
382    let l_modifiers = &*l_modifiers;
383    let r_modifiers = &*r_modifiers;
384
385    cx.emit_span_lint(
386        AMBIGUOUS_WIDE_POINTER_COMPARISONS,
387        e.span,
388        if is_eq_ne {
389            AmbiguousWidePointerComparisons::SpanfulEq {
390                addr_metadata_suggestion: (!is_dyn_comparison).then(|| {
391                    AmbiguousWidePointerComparisonsAddrMetadataSuggestion {
392                        krate,
393                        ne,
394                        deref_left,
395                        deref_right,
396                        l_modifiers,
397                        r_modifiers,
398                        left,
399                        middle,
400                        right,
401                    }
402                }),
403                addr_suggestion: AmbiguousWidePointerComparisonsAddrSuggestion {
404                    krate,
405                    ne,
406                    deref_left,
407                    deref_right,
408                    l_modifiers,
409                    r_modifiers,
410                    left,
411                    middle,
412                    right,
413                },
414            }
415        } else {
416            AmbiguousWidePointerComparisons::SpanfulCmp {
417                cast_suggestion: AmbiguousWidePointerComparisonsCastSuggestion {
418                    deref_left,
419                    deref_right,
420                    l_modifiers,
421                    r_modifiers,
422                    paren_left: if l_ty_refs != 0 { ")" } else { "" },
423                    paren_right: if r_ty_refs != 0 { ")" } else { "" },
424                    left_before: (l_ty_refs != 0).then_some(l_span.shrink_to_lo()),
425                    left_after: l_span.shrink_to_hi(),
426                    right_before: (r_ty_refs != 0).then_some(r_span.shrink_to_lo()),
427                    right_after: r_span.shrink_to_hi(),
428                },
429                expect_suggestion: AmbiguousWidePointerComparisonsExpectSuggestion {
430                    paren_left: if via_method_call { "" } else { "(" },
431                    paren_right: if via_method_call { "" } else { ")" },
432                    before: e.span.shrink_to_lo(),
433                    after: e.span.shrink_to_hi(),
434                },
435            }
436        },
437    );
438}
439
440fn lint_fn_pointer<'tcx>(
441    cx: &LateContext<'tcx>,
442    e: &'tcx hir::Expr<'tcx>,
443    cmpop: ComparisonOp,
444    l: &'tcx hir::Expr<'tcx>,
445    r: &'tcx hir::Expr<'tcx>,
446) {
447    let peel_refs = |mut ty: Ty<'tcx>| -> (Ty<'tcx>, usize) {
448        let mut refs = 0;
449
450        while let ty::Ref(_, inner_ty, _) = ty.kind() {
451            ty = *inner_ty;
452            refs += 1;
453        }
454
455        (ty, refs)
456    };
457
458    // Left and right operands can have borrows, remove them
459    let l = l.peel_borrows();
460    let r = r.peel_borrows();
461
462    let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else { return };
463    let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else { return };
464
465    // Remove any references as `==` will deref through them (and count the
466    // number of references removed, for latter).
467    let (l_ty, l_ty_refs) = peel_refs(l_ty);
468    let (r_ty, r_ty_refs) = peel_refs(r_ty);
469
470    if l_ty.is_fn() && r_ty.is_fn() {
471        // both operands are function pointers, fallthrough
472    } else if let ty::Adt(l_def, l_args) = l_ty.kind()
473        && let ty::Adt(r_def, r_args) = r_ty.kind()
474        && cx.tcx.is_lang_item(l_def.did(), LangItem::Option)
475        && cx.tcx.is_lang_item(r_def.did(), LangItem::Option)
476        && let Some(l_some_arg) = l_args.get(0)
477        && let Some(r_some_arg) = r_args.get(0)
478        && l_some_arg.expect_ty().is_fn()
479        && r_some_arg.expect_ty().is_fn()
480    {
481        // both operands are `Option<{function ptr}>`
482        return cx.emit_span_lint(
483            UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
484            e.span,
485            UnpredictableFunctionPointerComparisons::Warn,
486        );
487    } else {
488        // types are not function pointers, nothing to do
489        return;
490    }
491
492    // Let's try to suggest `ptr::fn_addr_eq` if/when possible.
493
494    let is_eq_ne = #[allow(non_exhaustive_omitted_patterns)] match cmpop {
    ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne) => true,
    _ => false,
}matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne));
495
496    if !is_eq_ne {
497        // Neither `==` nor `!=`, we can't suggest `ptr::fn_addr_eq`, just show the warning.
498        return cx.emit_span_lint(
499            UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
500            e.span,
501            UnpredictableFunctionPointerComparisons::Warn,
502        );
503    }
504
505    let (Some(l_span), Some(r_span)) =
506        (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
507    else {
508        // No appropriate spans for the left and right operands, just show the warning.
509        return cx.emit_span_lint(
510            UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
511            e.span,
512            UnpredictableFunctionPointerComparisons::Warn,
513        );
514    };
515
516    let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
517
518    // `ptr::fn_addr_eq` only works with raw pointer, deref any references.
519    let deref_left = &*"*".repeat(l_ty_refs);
520    let deref_right = &*"*".repeat(r_ty_refs);
521
522    let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
523    let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
524    let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
525
526    let Some(krate) = std_or_core(cx) else {
527        return cx.emit_span_lint(
528            UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
529            e.span,
530            UnpredictableFunctionPointerComparisons::Warn,
531        );
532    };
533
534    let sugg =
535        // We only check for a right cast as `FnDef` == `FnPtr` is not possible,
536        // only `FnPtr == FnDef` is possible.
537        if !r_ty.is_fn_ptr() {
538            let fn_sig = r_ty.fn_sig(cx.tcx);
539
540            UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEqWithCast {
541                krate,
542                ne,
543                fn_sig,
544                deref_left,
545                deref_right,
546                left,
547                middle,
548                right,
549            }
550        } else {
551            UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEq {
552                krate,
553                ne,
554                deref_left,
555                deref_right,
556                left,
557                middle,
558                right,
559            }
560        };
561
562    cx.emit_span_lint(
563        UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
564        e.span,
565        UnpredictableFunctionPointerComparisons::Suggestion { sugg },
566    );
567}
568
569impl<'tcx> LateLintPass<'tcx> for TypeLimits {
570    fn check_lit(
571        &mut self,
572        cx: &LateContext<'tcx>,
573        hir_id: HirId,
574        lit: hir::Lit,
575        is_negated_pat: bool,
576    ) {
577        let surrounding_negation = if is_negated_pat {
578            // In this case, lit.span refers to a `rustc_hir::hir::PatExprKind::Lit`,
579            // which includes the minus sign in front.
580            Some(lit.span)
581        } else if let Some(negation_info) = self.last_visited_negation
582            && negation_info.negated_id == hir_id
583        {
584            Some(negation_info.negation_span)
585        } else {
586            None
587        };
588        lint_literal(cx, hir_id, lit.span, &lit, surrounding_negation);
589    }
590
591    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
592        match e.kind {
593            hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
594                self.last_visited_negation =
595                    Some(NegationInfo { negation_span: e.span, negated_id: expr.hir_id });
596            }
597            hir::ExprKind::Binary(binop, ref l, ref r) => {
598                if is_comparison(binop.node) {
599                    if !check_limits(cx, binop.node, l, r) {
600                        cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
601                    } else {
602                        lint_nan(cx, e, binop.node, l, r);
603                        let cmpop = ComparisonOp::BinOp(binop.node);
604                        lint_wide_pointer(cx, e, cmpop, l, r);
605                        lint_fn_pointer(cx, e, cmpop, l, r);
606                    }
607                }
608            }
609            hir::ExprKind::Call(path, [l, r])
610                if let ExprKind::Path(ref qpath) = path.kind
611                    && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
612                    && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
613                    && let Some(cmpop) = diag_item_cmpop(diag_item) =>
614            {
615                lint_wide_pointer(cx, e, cmpop, l, r);
616                lint_fn_pointer(cx, e, cmpop, l, r);
617            }
618            hir::ExprKind::MethodCall(_, l, [r], _)
619                if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
620                    && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
621                    && let Some(cmpop) = diag_item_cmpop(diag_item) =>
622            {
623                lint_wide_pointer(cx, e, cmpop, l, r);
624                lint_fn_pointer(cx, e, cmpop, l, r);
625            }
626            _ => {}
627        };
628
629        fn is_valid<T: PartialOrd>(binop: hir::BinOpKind, v: T, min: T, max: T) -> bool {
630            match binop {
631                hir::BinOpKind::Lt => v > min && v <= max,
632                hir::BinOpKind::Le => v >= min && v < max,
633                hir::BinOpKind::Gt => v >= min && v < max,
634                hir::BinOpKind::Ge => v > min && v <= max,
635                hir::BinOpKind::Eq | hir::BinOpKind::Ne => v >= min && v <= max,
636                _ => ::rustc_span::macros::bug_impl(None, format_args!("impossible case reached"),
    Location::caller())bug!(),
637            }
638        }
639
640        fn rev_binop(binop: hir::BinOpKind) -> hir::BinOpKind {
641            match binop {
642                hir::BinOpKind::Lt => hir::BinOpKind::Gt,
643                hir::BinOpKind::Le => hir::BinOpKind::Ge,
644                hir::BinOpKind::Gt => hir::BinOpKind::Lt,
645                hir::BinOpKind::Ge => hir::BinOpKind::Le,
646                _ => binop,
647            }
648        }
649
650        fn check_limits(
651            cx: &LateContext<'_>,
652            binop: hir::BinOpKind,
653            l: &hir::Expr<'_>,
654            r: &hir::Expr<'_>,
655        ) -> bool {
656            let (lit, expr, swap) = match (&l.kind, &r.kind) {
657                (&hir::ExprKind::Lit(_), _) => (l, r, true),
658                (_, &hir::ExprKind::Lit(_)) => (r, l, false),
659                _ => return true,
660            };
661            // Normalize the binop so that the literal is always on the RHS in
662            // the comparison
663            let norm_binop = if swap { rev_binop(binop) } else { binop };
664            match *cx.typeck_results().node_type(expr.hir_id).kind() {
665                ty::Int(int_ty) => {
666                    let (min, max) = int_ty_range(int_ty);
667                    let lit_val: i128 = match lit.kind {
668                        hir::ExprKind::Lit(li) => match li.node {
669                            ast::LitKind::Int(
670                                v,
671                                ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed,
672                            ) => v.get() as i128,
673                            _ => return true,
674                        },
675                        _ => ::rustc_span::macros::bug_impl(None, format_args!("impossible case reached"),
    Location::caller())bug!(),
676                    };
677                    is_valid(norm_binop, lit_val, min, max)
678                }
679                ty::Uint(uint_ty) => {
680                    let (min, max): (u128, u128) = uint_ty_range(uint_ty);
681                    let lit_val: u128 = match lit.kind {
682                        hir::ExprKind::Lit(li) => match li.node {
683                            ast::LitKind::Int(v, _) => v.get(),
684                            _ => return true,
685                        },
686                        _ => ::rustc_span::macros::bug_impl(None, format_args!("impossible case reached"),
    Location::caller())bug!(),
687                    };
688                    is_valid(norm_binop, lit_val, min, max)
689                }
690                _ => true,
691            }
692        }
693
694        fn is_comparison(binop: hir::BinOpKind) -> bool {
695            #[allow(non_exhaustive_omitted_patterns)] match binop {
    hir::BinOpKind::Eq | hir::BinOpKind::Lt | hir::BinOpKind::Le |
        hir::BinOpKind::Ne | hir::BinOpKind::Ge | hir::BinOpKind::Gt => true,
    _ => false,
}matches!(
696                binop,
697                hir::BinOpKind::Eq
698                    | hir::BinOpKind::Lt
699                    | hir::BinOpKind::Le
700                    | hir::BinOpKind::Ne
701                    | hir::BinOpKind::Ge
702                    | hir::BinOpKind::Gt
703            )
704        }
705
706        fn diag_item_cmpop(diag_item: Symbol) -> Option<ComparisonOp> {
707            Some(match diag_item {
708                sym::cmp_ord_max => ComparisonOp::Other,
709                sym::cmp_ord_min => ComparisonOp::Other,
710                sym::ord_cmp_method => ComparisonOp::Other,
711                sym::cmp_partialeq_eq => ComparisonOp::BinOp(hir::BinOpKind::Eq),
712                sym::cmp_partialeq_ne => ComparisonOp::BinOp(hir::BinOpKind::Ne),
713                sym::cmp_partialord_cmp => ComparisonOp::Other,
714                sym::cmp_partialord_ge => ComparisonOp::BinOp(hir::BinOpKind::Ge),
715                sym::cmp_partialord_gt => ComparisonOp::BinOp(hir::BinOpKind::Gt),
716                sym::cmp_partialord_le => ComparisonOp::BinOp(hir::BinOpKind::Le),
717                sym::cmp_partialord_lt => ComparisonOp::BinOp(hir::BinOpKind::Lt),
718                _ => return None,
719            })
720        }
721    }
722}
723
724pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
725    tcx: TyCtxt<'tcx>,
726    def: ty::AdtDef<'tcx>,
727) -> bool {
728    {
        {
            'done:
                {
                for i in ::rustc_attr_ir::HasAttrs::get_attrs(def.did(), &tcx)
                    {
                    #[allow(unused_imports)]
                    use ::rustc_attr_ir::AttributeKind::*;
                    let i: &::rustc_attr_ir::Attribute = i;
                    match i {
                        ::rustc_attr_ir::Attribute::Parsed(RustcNonnullOptimizationGuaranteed)
                            => {
                            break 'done Some(());
                        }
                        ::rustc_attr_ir::Attribute::Unparsed(..) =>
                            {}
                            #[deny(unreachable_patterns)]
                            _ => {}
                    }
                }
                None
            }
        }
    }.is_some()find_attr!(tcx, def.did(), RustcNonnullOptimizationGuaranteed)
729}
730
731/// `repr(transparent)` structs can have a single non-1-ZST field, this function returns that
732/// field.
733pub(crate) fn transparent_newtype_field<'a, 'tcx>(
734    tcx: TyCtxt<'tcx>,
735    variant: &'a ty::VariantDef,
736) -> Option<&'a ty::FieldDef> {
737    let typing_env = ty::TypingEnv::non_body_analysis(tcx, variant.def_id);
738    variant.fields.iter().find(|field| {
739        let field_ty = tcx.type_of(field.did).instantiate_identity().skip_norm_wip();
740        let is_1zst =
741            tcx.layout_of(typing_env.as_query_input(field_ty)).is_ok_and(|layout| layout.is_1zst());
742        !is_1zst
743    })
744}
745
746/// Is type known to be non-null?
747fn ty_is_known_nonnull<'tcx>(
748    tcx: TyCtxt<'tcx>,
749    typing_env: ty::TypingEnv<'tcx>,
750    ty: Ty<'tcx>,
751) -> bool {
752    let ty = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty);
753
754    match ty.kind() {
755        ty::FnPtr(..) => true,
756        ty::Ref(..) => true,
757        ty::Adt(def, _) if def.is_box() => true,
758        ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => {
759            let marked_non_null = nonnull_optimization_guaranteed(tcx, *def);
760
761            if marked_non_null {
762                return true;
763            }
764
765            // `UnsafeCell` and `UnsafePinned` have their niche hidden.
766            if def.is_unsafe_cell() || def.is_unsafe_pinned() {
767                return false;
768            }
769
770            def.variants().iter().filter_map(|variant| transparent_newtype_field(tcx, variant)).any(
771                |field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args).skip_norm_wip()),
772            )
773        }
774        ty::Pat(base, pat) => {
775            ty_is_known_nonnull(tcx, typing_env, *base)
776                || pat_ty_is_known_nonnull(tcx, typing_env, *pat)
777        }
778        _ => false,
779    }
780}
781
782fn pat_ty_is_known_nonnull<'tcx>(
783    tcx: TyCtxt<'tcx>,
784    typing_env: ty::TypingEnv<'tcx>,
785    pat: ty::Pattern<'tcx>,
786) -> bool {
787    try {
788        match *pat {
789            ty::PatternKind::Range { start, end } => {
790                let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?;
791                let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?;
792
793                // This also works for negative numbers, as we just need
794                // to ensure we aren't wrapping over zero.
795                start > 0 && end >= start
796            }
797            ty::PatternKind::NotNull => true,
798            ty::PatternKind::Or(patterns) => {
799                patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat))
800            }
801        }
802    }
803    .unwrap_or_default()
804}
805
806/// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type.
807/// If the type passed in was not scalar, returns None.
808fn get_nullable_type<'tcx>(
809    tcx: TyCtxt<'tcx>,
810    typing_env: ty::TypingEnv<'tcx>,
811    ty: Ty<'tcx>,
812) -> Option<Ty<'tcx>> {
813    let ty = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty);
814
815    Some(match *ty.kind() {
816        ty::Adt(field_def, field_args) => {
817            let inner_field_ty = {
818                let mut first_non_zst_ty =
819                    field_def.variants().iter().filter_map(|v| transparent_newtype_field(tcx, v));
820                if true {
    {
        match (&first_non_zst_ty.clone().count(), &1) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val,
                        ::core::option::Option::Some(format_args!("Wrong number of fields for transparent type")));
                }
            }
        }
    };
};debug_assert_eq!(
821                    first_non_zst_ty.clone().count(),
822                    1,
823                    "Wrong number of fields for transparent type"
824                );
825                first_non_zst_ty
826                    .next_back()
827                    .expect("No non-zst fields in transparent type.")
828                    .ty(tcx, field_args)
829                    .skip_norm_wip()
830            };
831            return get_nullable_type(tcx, typing_env, inner_field_ty);
832        }
833        ty::Pat(base, ..) => return get_nullable_type(tcx, typing_env, base),
834        ty::Int(_) | ty::Uint(_) | ty::Char | ty::RawPtr(..) => ty,
835        // As these types are always non-null, the nullable equivalent of
836        // `Option<T>` of these types are their raw pointer counterparts.
837        ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl),
838        // There is no nullable equivalent for Rust's function pointers,
839        // you must use an `Option<fn(..) -> _>` to represent it.
840        ty::FnPtr(..) => ty,
841        // We should only ever reach this case if `ty_is_known_nonnull` is
842        // extended to other types.
843        ref unhandled => {
844            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/2311729bc1fb3f2e001e5c52c45eac94019a4fd5/compiler/rustc_lint/src/types.rs:844",
                        "rustc_lint::types", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/2311729bc1fb3f2e001e5c52c45eac94019a4fd5/compiler/rustc_lint/src/types.rs"),
                        ::tracing_core::__macro_support::Option::Some(844u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_lint::types"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("get_nullable_type: Unhandled scalar kind: {0:?} while checking {1:?}",
                                                    unhandled, ty) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(
845                "get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}",
846                unhandled, ty
847            );
848            return None;
849        }
850    })
851}
852
853/// A type is niche-optimization candidate iff:
854/// - Is a zero-sized type with alignment 1 (a “1-ZST”).
855/// - Is either a struct/tuple with no fields, or an enum with no variants.
856/// - Does not have the `#[non_exhaustive]` attribute.
857fn is_niche_optimization_candidate<'tcx>(
858    tcx: TyCtxt<'tcx>,
859    typing_env: ty::TypingEnv<'tcx>,
860    ty: Ty<'tcx>,
861) -> bool {
862    if tcx.layout_of(typing_env.as_query_input(ty)).is_ok_and(|layout| !layout.is_1zst()) {
863        return false;
864    }
865
866    match ty.kind() {
867        ty::Adt(ty_def, _) => {
868            let non_exhaustive = ty_def.is_variant_list_non_exhaustive();
869            let empty = (ty_def.is_struct() && ty_def.non_enum_variant().fields.is_empty())
870                || (ty_def.is_enum() && ty_def.variants().is_empty());
871
872            !non_exhaustive && empty
873        }
874        ty::Tuple(tys) => tys.is_empty(),
875        _ => false,
876    }
877}
878
879/// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
880/// can, return the type that `ty` can be safely converted to, otherwise return `None`.
881/// Currently restricted to function pointers, boxes, references, `core::num::NonZero`,
882/// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes.
883pub(crate) fn repr_nullable_ptr<'tcx>(
884    tcx: TyCtxt<'tcx>,
885    typing_env: ty::TypingEnv<'tcx>,
886    ty: Ty<'tcx>,
887) -> Option<Ty<'tcx>> {
888    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/2311729bc1fb3f2e001e5c52c45eac94019a4fd5/compiler/rustc_lint/src/types.rs:888",
                        "rustc_lint::types", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/2311729bc1fb3f2e001e5c52c45eac94019a4fd5/compiler/rustc_lint/src/types.rs"),
                        ::tracing_core::__macro_support::Option::Some(888u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_lint::types"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("is_repr_nullable_ptr(tcx, ty = {0:?})",
                                                    ty) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty);
889    match ty.kind() {
890        ty::Adt(ty_def, args) => {
891            let field_ty = match &ty_def.variants().raw[..] {
892                [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
893                    ([], [field]) | ([field], []) => field.ty(tcx, args).skip_norm_wip(),
894                    ([field1], [field2]) => {
895                        let ty1 = field1.ty(tcx, args).skip_norm_wip();
896                        let ty2 = field2.ty(tcx, args).skip_norm_wip();
897
898                        if is_niche_optimization_candidate(tcx, typing_env, ty1) {
899                            ty2
900                        } else if is_niche_optimization_candidate(tcx, typing_env, ty2) {
901                            ty1
902                        } else {
903                            return None;
904                        }
905                    }
906                    _ => return None,
907                },
908                _ => return None,
909            };
910
911            if !ty_is_known_nonnull(tcx, typing_env, field_ty) {
912                return None;
913            }
914
915            // At this point, the field's type is known to be nonnull and the parent enum is Option-like.
916            // If the computed size for the field and the enum are different, the nonnull optimization isn't
917            // being applied (and we've got a problem somewhere).
918            let compute_size_skeleton =
919                |t| SizeSkeleton::compute(t, tcx, typing_env, DUMMY_SP).ok();
920            if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) {
921                ::rustc_span::macros::bug_impl(None,
    format_args!("improper_ctypes: Option nonnull optimization not applied?"),
    Location::caller());bug!("improper_ctypes: Option nonnull optimization not applied?");
922            }
923
924            // Return the nullable type this Option-like enum can be safely represented with.
925            let field_ty_layout = tcx.layout_of(typing_env.as_query_input(field_ty));
926            if field_ty_layout.is_err() && !field_ty.has_non_region_param() {
927                ::rustc_span::macros::bug_impl(None,
    format_args!("should be able to compute the layout of non-polymorphic type"),
    Location::caller());bug!("should be able to compute the layout of non-polymorphic type");
928            }
929
930            let field_ty_abi = &field_ty_layout.ok()?.backend_repr;
931            if let BackendRepr::Scalar(field_ty_scalar) = field_ty_abi {
932                match field_ty_scalar.valid_range(&tcx) {
933                    WrappingRange { start: 0, end }
934                        if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 =>
935                    {
936                        return Some(get_nullable_type(tcx, typing_env, field_ty).expect(
937                            "known non-null scalar type should have a nullable representation",
938                        ));
939                    }
940                    WrappingRange { start: 1, .. } => {
941                        return Some(get_nullable_type(tcx, typing_env, field_ty).expect(
942                            "known non-null scalar type should have a nullable representation",
943                        ));
944                    }
945                    WrappingRange { start, end } => {
946                        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("Unhandled start and end range: ({0}, {1})", start,
                end)));
}unreachable!("Unhandled start and end range: ({}, {})", start, end)
947                    }
948                };
949            }
950            None
951        }
952        ty::Pat(base, pat) => get_nullable_type_from_pat(tcx, typing_env, *base, *pat),
953        _ => None,
954    }
955}
956
957fn get_nullable_type_from_pat<'tcx>(
958    tcx: TyCtxt<'tcx>,
959    typing_env: ty::TypingEnv<'tcx>,
960    base: Ty<'tcx>,
961    pat: ty::Pattern<'tcx>,
962) -> Option<Ty<'tcx>> {
963    match *pat {
964        ty::PatternKind::NotNull | ty::PatternKind::Range { .. } => {
965            get_nullable_type(tcx, typing_env, base)
966        }
967        ty::PatternKind::Or(patterns) => {
968            let first = get_nullable_type_from_pat(tcx, typing_env, base, patterns[0])?;
969            for &pat in &patterns[1..] {
970                {
    match (&first, &get_nullable_type_from_pat(tcx, typing_env, base, pat)?) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(first, get_nullable_type_from_pat(tcx, typing_env, base, pat)?);
971            }
972            Some(first)
973        }
974    }
975}
976
977pub struct VariantSizeDifferences;
#[automatically_derived]
impl ::core::marker::Copy for VariantSizeDifferences { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for VariantSizeDifferences { }
#[automatically_derived]
impl ::core::clone::Clone for VariantSizeDifferences {
    #[inline]
    fn clone(&self) -> Self { *self }
}
impl ::rustc_lint_defs::LintPass for VariantSizeDifferences {
    fn name(&self) -> &'static str { "VariantSizeDifferences" }
    fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [VARIANT_SIZE_DIFFERENCES]))
    }
}
impl VariantSizeDifferences {
    #[allow(unused)]
    pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [VARIANT_SIZE_DIFFERENCES]))
    }
}declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]);
978
979impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
980    fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
981        if let hir::ItemKind::Enum(_, _, ref enum_definition) = it.kind {
982            let t = cx.tcx.type_of(it.owner_id).instantiate_identity().skip_norm_wip();
983            let ty = cx.tcx.erase_and_anonymize_regions(t);
984            let Ok(layout) = cx.layout_of(ty) else { return };
985            let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, variants, .. } =
986                &layout.variants
987            else {
988                return;
989            };
990
991            let tag_size = tag.size(&cx.tcx).bytes();
992
993            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/2311729bc1fb3f2e001e5c52c45eac94019a4fd5/compiler/rustc_lint/src/types.rs:993",
                        "rustc_lint::types", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/2311729bc1fb3f2e001e5c52c45eac94019a4fd5/compiler/rustc_lint/src/types.rs"),
                        ::tracing_core::__macro_support::Option::Some(993u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_lint::types"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("enum `{0}` is {1} bytes large with layout:\n{2:#?}",
                                                    t, layout.size.bytes(), layout) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(
994                "enum `{}` is {} bytes large with layout:\n{:#?}",
995                t,
996                layout.size.bytes(),
997                layout
998            );
999
1000            let (largest, slargest, largest_index) = iter::zip(enum_definition.variants, variants)
1001                .map(|(variant, variant_layout)| {
1002                    // Subtract the size of the enum tag.
1003                    let bytes = variant_layout.size.bytes().saturating_sub(tag_size);
1004
1005                    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/2311729bc1fb3f2e001e5c52c45eac94019a4fd5/compiler/rustc_lint/src/types.rs:1005",
                        "rustc_lint::types", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/2311729bc1fb3f2e001e5c52c45eac94019a4fd5/compiler/rustc_lint/src/types.rs"),
                        ::tracing_core::__macro_support::Option::Some(1005u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_lint::types"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("- variant `{0}` is {1} bytes large",
                                                    variant.ident, bytes) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("- variant `{}` is {} bytes large", variant.ident, bytes);
1006                    bytes
1007                })
1008                .enumerate()
1009                .fold((0, 0, 0), |(l, s, li), (idx, size)| {
1010                    if size > l {
1011                        (size, l, idx)
1012                    } else if size > s {
1013                        (l, size, li)
1014                    } else {
1015                        (l, s, li)
1016                    }
1017                });
1018
1019            // We only warn if the largest variant is at least thrice as large as
1020            // the second-largest.
1021            if largest > slargest * 3 && slargest > 0 {
1022                cx.emit_span_lint(
1023                    VARIANT_SIZE_DIFFERENCES,
1024                    enum_definition.variants[largest_index].span,
1025                    VariantSizeDifferencesDiag { largest },
1026                );
1027            }
1028        }
1029    }
1030}
1031
1032#[doc = r" The `invalid_atomic_ordering` lint detects passing an `Ordering`"]
#[doc = r" to an atomic operation that does not support that ordering."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" # use core::sync::atomic::{AtomicU8, Ordering};"]
#[doc = r" let atom = AtomicU8::new(0);"]
#[doc = r" let value = atom.load(Ordering::Release);"]
#[doc = r" # let _ = value;"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" Some atomic operations are only supported for a subset of the"]
#[doc =
r" `atomic::Ordering` variants. Passing an unsupported variant will cause"]
#[doc =
r" an unconditional panic at runtime, which is detected by this lint."]
#[doc = r""]
#[doc =
r" This lint will trigger in the following cases: (where `AtomicType` is an"]
#[doc = r" atomic type from `core::sync::atomic`, such as `AtomicBool`,"]
#[doc = r" `AtomicPtr`, `AtomicUsize`, or any of the other integer atomics)."]
#[doc = r""]
#[doc = r" - Passing `Ordering::Acquire` or `Ordering::AcqRel` to"]
#[doc = r"   `AtomicType::store`."]
#[doc = r""]
#[doc = r" - Passing `Ordering::Release` or `Ordering::AcqRel` to"]
#[doc = r"   `AtomicType::load`."]
#[doc = r""]
#[doc = r" - Passing `Ordering::Relaxed` to `core::sync::atomic::fence` or"]
#[doc = r"   `core::sync::atomic::compiler_fence`."]
#[doc = r""]
#[doc =
r" - Passing `Ordering::Release` or `Ordering::AcqRel` as the failure"]
#[doc = r"   ordering for any of `AtomicType::compare_exchange`,"]
#[doc = r"   `AtomicType::compare_exchange_weak`, `AtomicType::update`, or"]
#[doc = r"   `AtomicType::try_update`."]
static INVALID_ATOMIC_ORDERING: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "INVALID_ATOMIC_ORDERING",
            default_level: ::rustc_lint_defs::Deny,
            desc: "usage of invalid atomic ordering in atomic operations and memory fences",
            is_externally_loaded: false,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
1033    /// The `invalid_atomic_ordering` lint detects passing an `Ordering`
1034    /// to an atomic operation that does not support that ordering.
1035    ///
1036    /// ### Example
1037    ///
1038    /// ```rust,compile_fail
1039    /// # use core::sync::atomic::{AtomicU8, Ordering};
1040    /// let atom = AtomicU8::new(0);
1041    /// let value = atom.load(Ordering::Release);
1042    /// # let _ = value;
1043    /// ```
1044    ///
1045    /// {{produces}}
1046    ///
1047    /// ### Explanation
1048    ///
1049    /// Some atomic operations are only supported for a subset of the
1050    /// `atomic::Ordering` variants. Passing an unsupported variant will cause
1051    /// an unconditional panic at runtime, which is detected by this lint.
1052    ///
1053    /// This lint will trigger in the following cases: (where `AtomicType` is an
1054    /// atomic type from `core::sync::atomic`, such as `AtomicBool`,
1055    /// `AtomicPtr`, `AtomicUsize`, or any of the other integer atomics).
1056    ///
1057    /// - Passing `Ordering::Acquire` or `Ordering::AcqRel` to
1058    ///   `AtomicType::store`.
1059    ///
1060    /// - Passing `Ordering::Release` or `Ordering::AcqRel` to
1061    ///   `AtomicType::load`.
1062    ///
1063    /// - Passing `Ordering::Relaxed` to `core::sync::atomic::fence` or
1064    ///   `core::sync::atomic::compiler_fence`.
1065    ///
1066    /// - Passing `Ordering::Release` or `Ordering::AcqRel` as the failure
1067    ///   ordering for any of `AtomicType::compare_exchange`,
1068    ///   `AtomicType::compare_exchange_weak`, `AtomicType::update`, or
1069    ///   `AtomicType::try_update`.
1070    INVALID_ATOMIC_ORDERING,
1071    Deny,
1072    "usage of invalid atomic ordering in atomic operations and memory fences"
1073}
1074
1075pub struct InvalidAtomicOrdering;
#[automatically_derived]
impl ::core::marker::Copy for InvalidAtomicOrdering { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for InvalidAtomicOrdering { }
#[automatically_derived]
impl ::core::clone::Clone for InvalidAtomicOrdering {
    #[inline]
    fn clone(&self) -> Self { *self }
}
impl ::rustc_lint_defs::LintPass for InvalidAtomicOrdering {
    fn name(&self) -> &'static str { "InvalidAtomicOrdering" }
    fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [INVALID_ATOMIC_ORDERING]))
    }
}
impl InvalidAtomicOrdering {
    #[allow(unused)]
    pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [INVALID_ATOMIC_ORDERING]))
    }
}declare_lint_pass!(InvalidAtomicOrdering => [INVALID_ATOMIC_ORDERING]);
1076
1077impl InvalidAtomicOrdering {
1078    fn inherent_atomic_method_call<'hir>(
1079        cx: &LateContext<'_>,
1080        expr: &Expr<'hir>,
1081        recognized_names: &[Symbol], // used for fast path calculation
1082    ) -> Option<(Symbol, &'hir [Expr<'hir>])> {
1083        if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind
1084            && recognized_names.contains(&method_path.ident.name)
1085            && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
1086            // skip extension traits, only lint functions from the standard library
1087            && let Some(impl_did) = cx.tcx.inherent_impl_of_assoc(m_def_id)
1088            && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().skip_norm_wip().ty_adt_def()
1089            && cx.tcx.is_diagnostic_item(sym::Atomic, adt.did())
1090        {
1091            return Some((method_path.ident.name, args));
1092        }
1093        None
1094    }
1095
1096    fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol> {
1097        let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None };
1098        let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?;
1099        let tcx = cx.tcx;
1100        let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering);
1101        let name = tcx.item_name(did);
1102        let parent = tcx.parent(did);
1103        [sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find(
1104            |&ordering| {
1105                name == ordering
1106                    && (Some(parent) == atomic_ordering
1107                            // needed in case this is a ctor, not a variant
1108                            || tcx.opt_parent(parent) == atomic_ordering)
1109            },
1110        )
1111    }
1112
1113    fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
1114        if let Some((method, args)) =
1115            Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
1116            && let Some((ordering_arg, invalid_ordering)) = match method {
1117                sym::load => Some((&args[0], sym::Release)),
1118                sym::store => Some((&args[1], sym::Acquire)),
1119                _ => None,
1120            }
1121            && let Some(ordering) = Self::match_ordering(cx, ordering_arg)
1122            && (ordering == invalid_ordering || ordering == sym::AcqRel)
1123        {
1124            if method == sym::load {
1125                cx.emit_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad);
1126            } else {
1127                cx.emit_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore);
1128            };
1129        }
1130    }
1131
1132    fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
1133        if let ExprKind::Call(func, args) = expr.kind
1134            && let ExprKind::Path(ref func_qpath) = func.kind
1135            && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
1136            && #[allow(non_exhaustive_omitted_patterns)] match cx.tcx.get_diagnostic_name(def_id)
    {
    Some(sym::fence | sym::compiler_fence) => true,
    _ => false,
}matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
1137            && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
1138        {
1139            cx.emit_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence);
1140        }
1141    }
1142
1143    fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
1144        let Some((method, args)) = Self::inherent_atomic_method_call(
1145            cx,
1146            expr,
1147            &[
1148                sym::update,
1149                sym::try_update,
1150                sym::fetch_update,
1151                sym::compare_exchange,
1152                sym::compare_exchange_weak,
1153            ],
1154        ) else {
1155            return;
1156        };
1157
1158        let fail_order_arg = match method {
1159            sym::update | sym::try_update | sym::fetch_update => &args[1],
1160            sym::compare_exchange | sym::compare_exchange_weak => &args[3],
1161            _ => return,
1162        };
1163
1164        let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
1165
1166        if #[allow(non_exhaustive_omitted_patterns)] match fail_ordering {
    sym::Release | sym::AcqRel => true,
    _ => false,
}matches!(fail_ordering, sym::Release | sym::AcqRel) {
1167            cx.emit_span_lint(
1168                INVALID_ATOMIC_ORDERING,
1169                fail_order_arg.span,
1170                InvalidAtomicOrderingDiag { method, fail_order_arg_span: fail_order_arg.span },
1171            );
1172        }
1173    }
1174}
1175
1176impl<'tcx> LateLintPass<'tcx> for InvalidAtomicOrdering {
1177    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1178        Self::check_atomic_load_store(cx, expr);
1179        Self::check_memory_fence(cx, expr);
1180        Self::check_atomic_compare_exchange(cx, expr);
1181    }
1182}