• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::{
2     fluent_generated as fluent,
3     lints::{
4         AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes,
5         InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion,
6         OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub,
7         OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral,
8         OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange,
9         VariantSizeDifferencesDiag,
10     },
11 };
12 use crate::{LateContext, LateLintPass, LintContext};
13 use rustc_ast as ast;
14 use rustc_attr as attr;
15 use rustc_data_structures::fx::FxHashSet;
16 use rustc_errors::DiagnosticMessage;
17 use rustc_hir as hir;
18 use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
19 use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
20 use rustc_middle::ty::subst::SubstsRef;
21 use rustc_middle::ty::{
22     self, AdtKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
23 };
24 use rustc_span::def_id::LocalDefId;
25 use rustc_span::source_map;
26 use rustc_span::symbol::sym;
27 use rustc_span::{Span, Symbol};
28 use rustc_target::abi::{Abi, Size, WrappingRange};
29 use rustc_target::abi::{Integer, TagEncoding, Variants};
30 use rustc_target::spec::abi::Abi as SpecAbi;
31 
32 use std::iter;
33 use std::ops::ControlFlow;
34 
35 declare_lint! {
36     /// The `unused_comparisons` lint detects comparisons made useless by
37     /// limits of the types involved.
38     ///
39     /// ### Example
40     ///
41     /// ```rust
42     /// fn foo(x: u8) {
43     ///     x >= 0;
44     /// }
45     /// ```
46     ///
47     /// {{produces}}
48     ///
49     /// ### Explanation
50     ///
51     /// A useless comparison may indicate a mistake, and should be fixed or
52     /// removed.
53     UNUSED_COMPARISONS,
54     Warn,
55     "comparisons made useless by limits of the types involved"
56 }
57 
58 declare_lint! {
59     /// The `overflowing_literals` lint detects literal out of range for its
60     /// type.
61     ///
62     /// ### Example
63     ///
64     /// ```rust,compile_fail
65     /// let x: u8 = 1000;
66     /// ```
67     ///
68     /// {{produces}}
69     ///
70     /// ### Explanation
71     ///
72     /// It is usually a mistake to use a literal that overflows the type where
73     /// it is used. Either use a literal that is within range, or change the
74     /// type to be within the range of the literal.
75     OVERFLOWING_LITERALS,
76     Deny,
77     "literal out of range for its type"
78 }
79 
80 declare_lint! {
81     /// The `variant_size_differences` lint detects enums with widely varying
82     /// variant sizes.
83     ///
84     /// ### Example
85     ///
86     /// ```rust,compile_fail
87     /// #![deny(variant_size_differences)]
88     /// enum En {
89     ///     V0(u8),
90     ///     VBig([u8; 1024]),
91     /// }
92     /// ```
93     ///
94     /// {{produces}}
95     ///
96     /// ### Explanation
97     ///
98     /// It can be a mistake to add a variant to an enum that is much larger
99     /// than the other variants, bloating the overall size required for all
100     /// variants. This can impact performance and memory usage. This is
101     /// triggered if one variant is more than 3 times larger than the
102     /// second-largest variant.
103     ///
104     /// Consider placing the large variant's contents on the heap (for example
105     /// via [`Box`]) to keep the overall size of the enum itself down.
106     ///
107     /// This lint is "allow" by default because it can be noisy, and may not be
108     /// an actual problem. Decisions about this should be guided with
109     /// profiling and benchmarking.
110     ///
111     /// [`Box`]: https://doc.rust-lang.org/std/boxed/index.html
112     VARIANT_SIZE_DIFFERENCES,
113     Allow,
114     "detects enums with widely varying variant sizes"
115 }
116 
117 declare_lint! {
118     /// The `invalid_nan_comparisons` lint checks comparison with `f32::NAN` or `f64::NAN`
119     /// as one of the operand.
120     ///
121     /// ### Example
122     ///
123     /// ```rust
124     /// let a = 2.3f32;
125     /// if a == f32::NAN {}
126     /// ```
127     ///
128     /// {{produces}}
129     ///
130     /// ### Explanation
131     ///
132     /// NaN does not compare meaningfully to anything – not
133     /// even itself – so those comparisons are always false.
134     INVALID_NAN_COMPARISONS,
135     Warn,
136     "detects invalid floating point NaN comparisons"
137 }
138 
139 #[derive(Copy, Clone)]
140 pub struct TypeLimits {
141     /// Id of the last visited negated expression
142     negated_expr_id: Option<hir::HirId>,
143 }
144 
145 impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS, INVALID_NAN_COMPARISONS]);
146 
147 impl TypeLimits {
new() -> TypeLimits148     pub fn new() -> TypeLimits {
149         TypeLimits { negated_expr_id: None }
150     }
151 }
152 
153 /// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint (`expr..MAX+1`).
154 /// Returns `true` iff the lint was emitted.
lint_overflowing_range_endpoint<'tcx>( cx: &LateContext<'tcx>, lit: &hir::Lit, lit_val: u128, max: u128, expr: &'tcx hir::Expr<'tcx>, ty: &str, ) -> bool155 fn lint_overflowing_range_endpoint<'tcx>(
156     cx: &LateContext<'tcx>,
157     lit: &hir::Lit,
158     lit_val: u128,
159     max: u128,
160     expr: &'tcx hir::Expr<'tcx>,
161     ty: &str,
162 ) -> bool {
163     // Look past casts to support cases like `0..256 as u8`
164     let (expr, lit_span) = if let Node::Expr(par_expr) = cx.tcx.hir().get(cx.tcx.hir().parent_id(expr.hir_id))
165       && let ExprKind::Cast(_, _) = par_expr.kind {
166         (par_expr, expr.span)
167     } else {
168         (expr, expr.span)
169     };
170 
171     // We only want to handle exclusive (`..`) ranges,
172     // which are represented as `ExprKind::Struct`.
173     let par_id = cx.tcx.hir().parent_id(expr.hir_id);
174     let Node::ExprField(field) = cx.tcx.hir().get(par_id) else { return false };
175     let Node::Expr(struct_expr) = cx.tcx.hir().get_parent(field.hir_id) else { return false };
176     if !is_range_literal(struct_expr) {
177         return false;
178     };
179     let ExprKind::Struct(_, eps, _) = &struct_expr.kind else { return false };
180     if eps.len() != 2 {
181         return false;
182     }
183 
184     // We can suggest using an inclusive range
185     // (`..=`) instead only if it is the `end` that is
186     // overflowing and only by 1.
187     if !(eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max) {
188         return false;
189     };
190 
191     use rustc_ast::{LitIntType, LitKind};
192     let suffix = match lit.node {
193         LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
194         LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
195         LitKind::Int(_, LitIntType::Unsuffixed) => "",
196         _ => bug!(),
197     };
198 
199     let sub_sugg = if expr.span.lo() == lit_span.lo() {
200         let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) else { return false };
201         UseInclusiveRange::WithoutParen {
202             sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()),
203             start,
204             literal: lit_val - 1,
205             suffix,
206         }
207     } else {
208         UseInclusiveRange::WithParen {
209             eq_sugg: expr.span.shrink_to_lo(),
210             lit_sugg: lit_span,
211             literal: lit_val - 1,
212             suffix,
213         }
214     };
215 
216     cx.emit_spanned_lint(
217         OVERFLOWING_LITERALS,
218         struct_expr.span,
219         RangeEndpointOutOfRange { ty, sub: sub_sugg },
220     );
221 
222     // We've just emitted a lint, special cased for `(...)..MAX+1` ranges,
223     // return `true` so the callers don't also emit a lint
224     true
225 }
226 
227 // For `isize` & `usize`, be conservative with the warnings, so that the
228 // warnings are consistent between 32- and 64-bit platforms.
int_ty_range(int_ty: ty::IntTy) -> (i128, i128)229 fn int_ty_range(int_ty: ty::IntTy) -> (i128, i128) {
230     match int_ty {
231         ty::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()),
232         ty::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()),
233         ty::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()),
234         ty::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()),
235         ty::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()),
236         ty::IntTy::I128 => (i128::MIN, i128::MAX),
237     }
238 }
239 
uint_ty_range(uint_ty: ty::UintTy) -> (u128, u128)240 fn uint_ty_range(uint_ty: ty::UintTy) -> (u128, u128) {
241     let max = match uint_ty {
242         ty::UintTy::Usize => u64::MAX.into(),
243         ty::UintTy::U8 => u8::MAX.into(),
244         ty::UintTy::U16 => u16::MAX.into(),
245         ty::UintTy::U32 => u32::MAX.into(),
246         ty::UintTy::U64 => u64::MAX.into(),
247         ty::UintTy::U128 => u128::MAX,
248     };
249     (0, max)
250 }
251 
get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option<String>252 fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option<String> {
253     let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?;
254     let firstch = src.chars().next()?;
255 
256     if firstch == '0' {
257         match src.chars().nth(1) {
258             Some('x' | 'b') => return Some(src),
259             _ => return None,
260         }
261     }
262 
263     None
264 }
265 
report_bin_hex_error( cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: attr::IntType, size: Size, repr_str: String, val: u128, negative: bool, )266 fn report_bin_hex_error(
267     cx: &LateContext<'_>,
268     expr: &hir::Expr<'_>,
269     ty: attr::IntType,
270     size: Size,
271     repr_str: String,
272     val: u128,
273     negative: bool,
274 ) {
275     let (t, actually) = match ty {
276         attr::IntType::SignedInt(t) => {
277             let actually = if negative {
278                 -(size.sign_extend(val) as i128)
279             } else {
280                 size.sign_extend(val) as i128
281             };
282             (t.name_str(), actually.to_string())
283         }
284         attr::IntType::UnsignedInt(t) => {
285             let actually = size.truncate(val);
286             (t.name_str(), actually.to_string())
287         }
288     };
289     let sign =
290         if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive };
291     let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map(
292         |suggestion_ty| {
293             if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
294                 let (sans_suffix, _) = repr_str.split_at(pos);
295                 OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix }
296             } else {
297                 OverflowingBinHexSub::Help { suggestion_ty }
298             }
299         },
300     );
301     let sign_bit_sub = (!negative)
302         .then(|| {
303             let ty::Int(int_ty) = cx.typeck_results().node_type(expr.hir_id).kind() else {
304                 return None;
305             };
306 
307             let Some(bit_width) = int_ty.bit_width() else {
308                 return None; // isize case
309             };
310 
311             // Skip if sign bit is not set
312             if (val & (1 << (bit_width - 1))) == 0 {
313                 return None;
314             }
315 
316             let lit_no_suffix =
317                 if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
318                     repr_str.split_at(pos).0
319                 } else {
320                     &repr_str
321                 };
322 
323             Some(OverflowingBinHexSignBitSub {
324                 span: expr.span,
325                 lit_no_suffix,
326                 negative_val: actually.clone(),
327                 int_ty: int_ty.name_str(),
328                 uint_ty: int_ty.to_unsigned().name_str(),
329             })
330         })
331         .flatten();
332 
333     cx.emit_spanned_lint(
334         OVERFLOWING_LITERALS,
335         expr.span,
336         OverflowingBinHex {
337             ty: t,
338             lit: repr_str.clone(),
339             dec: val,
340             actually,
341             sign,
342             sub,
343             sign_bit_sub,
344         },
345     )
346 }
347 
348 // This function finds the next fitting type and generates a suggestion string.
349 // It searches for fitting types in the following way (`X < Y`):
350 //  - `iX`: if literal fits in `uX` => `uX`, else => `iY`
351 //  - `-iX` => `iY`
352 //  - `uX` => `uY`
353 //
354 // No suggestion for: `isize`, `usize`.
get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str>355 fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> {
356     use ty::IntTy::*;
357     use ty::UintTy::*;
358     macro_rules! find_fit {
359         ($ty:expr, $val:expr, $negative:expr,
360          $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => {
361             {
362                 let _neg = if negative { 1 } else { 0 };
363                 match $ty {
364                     $($type => {
365                         $(if !negative && val <= uint_ty_range($utypes).1 {
366                             return Some($utypes.name_str())
367                         })*
368                         $(if val <= int_ty_range($itypes).1 as u128 + _neg {
369                             return Some($itypes.name_str())
370                         })*
371                         None
372                     },)+
373                     _ => None
374                 }
375             }
376         }
377     }
378     match t.kind() {
379         ty::Int(i) => find_fit!(i, val, negative,
380                       I8 => [U8] => [I16, I32, I64, I128],
381                       I16 => [U16] => [I32, I64, I128],
382                       I32 => [U32] => [I64, I128],
383                       I64 => [U64] => [I128],
384                       I128 => [U128] => []),
385         ty::Uint(u) => find_fit!(u, val, negative,
386                       U8 => [U8, U16, U32, U64, U128] => [],
387                       U16 => [U16, U32, U64, U128] => [],
388                       U32 => [U32, U64, U128] => [],
389                       U64 => [U64, U128] => [],
390                       U128 => [U128] => []),
391         _ => None,
392     }
393 }
394 
lint_int_literal<'tcx>( cx: &LateContext<'tcx>, type_limits: &TypeLimits, e: &'tcx hir::Expr<'tcx>, lit: &hir::Lit, t: ty::IntTy, v: u128, )395 fn lint_int_literal<'tcx>(
396     cx: &LateContext<'tcx>,
397     type_limits: &TypeLimits,
398     e: &'tcx hir::Expr<'tcx>,
399     lit: &hir::Lit,
400     t: ty::IntTy,
401     v: u128,
402 ) {
403     let int_type = t.normalize(cx.sess().target.pointer_width);
404     let (min, max) = int_ty_range(int_type);
405     let max = max as u128;
406     let negative = type_limits.negated_expr_id == Some(e.hir_id);
407 
408     // Detect literal value out of range [min, max] inclusive
409     // avoiding use of -min to prevent overflow/panic
410     if (negative && v > max + 1) || (!negative && v > max) {
411         if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
412             report_bin_hex_error(
413                 cx,
414                 e,
415                 attr::IntType::SignedInt(ty::ast_int_ty(t)),
416                 Integer::from_int_ty(cx, t).size(),
417                 repr_str,
418                 v,
419                 negative,
420             );
421             return;
422         }
423 
424         if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) {
425             // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`.
426             return;
427         }
428 
429         let lit = cx
430             .sess()
431             .source_map()
432             .span_to_snippet(lit.span)
433             .expect("must get snippet from literal");
434         let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
435             .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty });
436 
437         cx.emit_spanned_lint(
438             OVERFLOWING_LITERALS,
439             e.span,
440             OverflowingInt { ty: t.name_str(), lit, min, max, help },
441         );
442     }
443 }
444 
lint_uint_literal<'tcx>( cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>, lit: &hir::Lit, t: ty::UintTy, )445 fn lint_uint_literal<'tcx>(
446     cx: &LateContext<'tcx>,
447     e: &'tcx hir::Expr<'tcx>,
448     lit: &hir::Lit,
449     t: ty::UintTy,
450 ) {
451     let uint_type = t.normalize(cx.sess().target.pointer_width);
452     let (min, max) = uint_ty_range(uint_type);
453     let lit_val: u128 = match lit.node {
454         // _v is u8, within range by definition
455         ast::LitKind::Byte(_v) => return,
456         ast::LitKind::Int(v, _) => v,
457         _ => bug!(),
458     };
459     if lit_val < min || lit_val > max {
460         let parent_id = cx.tcx.hir().parent_id(e.hir_id);
461         if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) {
462             match par_e.kind {
463                 hir::ExprKind::Cast(..) => {
464                     if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() {
465                         cx.emit_spanned_lint(
466                             OVERFLOWING_LITERALS,
467                             par_e.span,
468                             OnlyCastu8ToChar { span: par_e.span, literal: lit_val },
469                         );
470                         return;
471                     }
472                 }
473                 _ => {}
474             }
475         }
476         if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) {
477             // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`.
478             return;
479         }
480         if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
481             report_bin_hex_error(
482                 cx,
483                 e,
484                 attr::IntType::UnsignedInt(ty::ast_uint_ty(t)),
485                 Integer::from_uint_ty(cx, t).size(),
486                 repr_str,
487                 lit_val,
488                 false,
489             );
490             return;
491         }
492         cx.emit_spanned_lint(
493             OVERFLOWING_LITERALS,
494             e.span,
495             OverflowingUInt {
496                 ty: t.name_str(),
497                 lit: cx
498                     .sess()
499                     .source_map()
500                     .span_to_snippet(lit.span)
501                     .expect("must get snippet from literal"),
502                 min,
503                 max,
504             },
505         );
506     }
507 }
508 
lint_literal<'tcx>( cx: &LateContext<'tcx>, type_limits: &TypeLimits, e: &'tcx hir::Expr<'tcx>, lit: &hir::Lit, )509 fn lint_literal<'tcx>(
510     cx: &LateContext<'tcx>,
511     type_limits: &TypeLimits,
512     e: &'tcx hir::Expr<'tcx>,
513     lit: &hir::Lit,
514 ) {
515     match *cx.typeck_results().node_type(e.hir_id).kind() {
516         ty::Int(t) => {
517             match lit.node {
518                 ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => {
519                     lint_int_literal(cx, type_limits, e, lit, t, v)
520                 }
521                 _ => bug!(),
522             };
523         }
524         ty::Uint(t) => lint_uint_literal(cx, e, lit, t),
525         ty::Float(t) => {
526             let is_infinite = match lit.node {
527                 ast::LitKind::Float(v, _) => match t {
528                     ty::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite),
529                     ty::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite),
530                 },
531                 _ => bug!(),
532             };
533             if is_infinite == Ok(true) {
534                 cx.emit_spanned_lint(
535                     OVERFLOWING_LITERALS,
536                     e.span,
537                     OverflowingLiteral {
538                         ty: t.name_str(),
539                         lit: cx
540                             .sess()
541                             .source_map()
542                             .span_to_snippet(lit.span)
543                             .expect("must get snippet from literal"),
544                     },
545                 );
546             }
547         }
548         _ => {}
549     }
550 }
551 
lint_nan<'tcx>( cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>, binop: hir::BinOp, l: &'tcx hir::Expr<'tcx>, r: &'tcx hir::Expr<'tcx>, )552 fn lint_nan<'tcx>(
553     cx: &LateContext<'tcx>,
554     e: &'tcx hir::Expr<'tcx>,
555     binop: hir::BinOp,
556     l: &'tcx hir::Expr<'tcx>,
557     r: &'tcx hir::Expr<'tcx>,
558 ) {
559     fn is_nan(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
560         let expr = expr.peel_blocks().peel_borrows();
561         match expr.kind {
562             ExprKind::Path(qpath) => {
563                 let Some(def_id) = cx.typeck_results().qpath_res(&qpath, expr.hir_id).opt_def_id() else { return false; };
564 
565                 matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::f32_nan | sym::f64_nan))
566             }
567             _ => false,
568         }
569     }
570 
571     fn eq_ne(
572         e: &hir::Expr<'_>,
573         l: &hir::Expr<'_>,
574         r: &hir::Expr<'_>,
575         f: impl FnOnce(Span, Span) -> InvalidNanComparisonsSuggestion,
576     ) -> InvalidNanComparisons {
577         let suggestion =
578             if let Some(l_span) = l.span.find_ancestor_inside(e.span) &&
579                 let Some(r_span) = r.span.find_ancestor_inside(e.span) {
580                 f(l_span, r_span)
581             } else {
582                 InvalidNanComparisonsSuggestion::Spanless
583             };
584 
585         InvalidNanComparisons::EqNe { suggestion }
586     }
587 
588     let lint = match binop.node {
589         hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => {
590             eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
591                 nan_plus_binop: l_span.until(r_span),
592                 float: r_span.shrink_to_hi(),
593                 neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
594             })
595         }
596         hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => {
597             eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
598                 nan_plus_binop: l_span.shrink_to_hi().to(r_span),
599                 float: l_span.shrink_to_hi(),
600                 neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
601             })
602         }
603         hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge
604             if is_nan(cx, l) || is_nan(cx, r) =>
605         {
606             InvalidNanComparisons::LtLeGtGe
607         }
608         _ => return,
609     };
610 
611     cx.emit_spanned_lint(INVALID_NAN_COMPARISONS, e.span, lint);
612 }
613 
614 impl<'tcx> LateLintPass<'tcx> for TypeLimits {
check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>)615     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
616         match e.kind {
617             hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => {
618                 // propagate negation, if the negation itself isn't negated
619                 if self.negated_expr_id != Some(e.hir_id) {
620                     self.negated_expr_id = Some(expr.hir_id);
621                 }
622             }
623             hir::ExprKind::Binary(binop, ref l, ref r) => {
624                 if is_comparison(binop) {
625                     if !check_limits(cx, binop, &l, &r) {
626                         cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
627                     } else {
628                         lint_nan(cx, e, binop, l, r);
629                     }
630                 }
631             }
632             hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit),
633             _ => {}
634         };
635 
636         fn is_valid<T: PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
637             match binop.node {
638                 hir::BinOpKind::Lt => v > min && v <= max,
639                 hir::BinOpKind::Le => v >= min && v < max,
640                 hir::BinOpKind::Gt => v >= min && v < max,
641                 hir::BinOpKind::Ge => v > min && v <= max,
642                 hir::BinOpKind::Eq | hir::BinOpKind::Ne => v >= min && v <= max,
643                 _ => bug!(),
644             }
645         }
646 
647         fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
648             source_map::respan(
649                 binop.span,
650                 match binop.node {
651                     hir::BinOpKind::Lt => hir::BinOpKind::Gt,
652                     hir::BinOpKind::Le => hir::BinOpKind::Ge,
653                     hir::BinOpKind::Gt => hir::BinOpKind::Lt,
654                     hir::BinOpKind::Ge => hir::BinOpKind::Le,
655                     _ => return binop,
656                 },
657             )
658         }
659 
660         fn check_limits(
661             cx: &LateContext<'_>,
662             binop: hir::BinOp,
663             l: &hir::Expr<'_>,
664             r: &hir::Expr<'_>,
665         ) -> bool {
666             let (lit, expr, swap) = match (&l.kind, &r.kind) {
667                 (&hir::ExprKind::Lit(_), _) => (l, r, true),
668                 (_, &hir::ExprKind::Lit(_)) => (r, l, false),
669                 _ => return true,
670             };
671             // Normalize the binop so that the literal is always on the RHS in
672             // the comparison
673             let norm_binop = if swap { rev_binop(binop) } else { binop };
674             match *cx.typeck_results().node_type(expr.hir_id).kind() {
675                 ty::Int(int_ty) => {
676                     let (min, max) = int_ty_range(int_ty);
677                     let lit_val: i128 = match lit.kind {
678                         hir::ExprKind::Lit(ref li) => match li.node {
679                             ast::LitKind::Int(
680                                 v,
681                                 ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed,
682                             ) => v as i128,
683                             _ => return true,
684                         },
685                         _ => bug!(),
686                     };
687                     is_valid(norm_binop, lit_val, min, max)
688                 }
689                 ty::Uint(uint_ty) => {
690                     let (min, max): (u128, u128) = uint_ty_range(uint_ty);
691                     let lit_val: u128 = match lit.kind {
692                         hir::ExprKind::Lit(ref li) => match li.node {
693                             ast::LitKind::Int(v, _) => v,
694                             _ => return true,
695                         },
696                         _ => bug!(),
697                     };
698                     is_valid(norm_binop, lit_val, min, max)
699                 }
700                 _ => true,
701             }
702         }
703 
704         fn is_comparison(binop: hir::BinOp) -> bool {
705             matches!(
706                 binop.node,
707                 hir::BinOpKind::Eq
708                     | hir::BinOpKind::Lt
709                     | hir::BinOpKind::Le
710                     | hir::BinOpKind::Ne
711                     | hir::BinOpKind::Ge
712                     | hir::BinOpKind::Gt
713             )
714         }
715     }
716 }
717 
718 declare_lint! {
719     /// The `improper_ctypes` lint detects incorrect use of types in foreign
720     /// modules.
721     ///
722     /// ### Example
723     ///
724     /// ```rust
725     /// extern "C" {
726     ///     static STATIC: String;
727     /// }
728     /// ```
729     ///
730     /// {{produces}}
731     ///
732     /// ### Explanation
733     ///
734     /// The compiler has several checks to verify that types used in `extern`
735     /// blocks are safe and follow certain rules to ensure proper
736     /// compatibility with the foreign interfaces. This lint is issued when it
737     /// detects a probable mistake in a definition. The lint usually should
738     /// provide a description of the issue, along with possibly a hint on how
739     /// to resolve it.
740     IMPROPER_CTYPES,
741     Warn,
742     "proper use of libc types in foreign modules"
743 }
744 
745 declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]);
746 
747 declare_lint! {
748     /// The `improper_ctypes_definitions` lint detects incorrect use of
749     /// [`extern` function] definitions.
750     ///
751     /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
752     ///
753     /// ### Example
754     ///
755     /// ```rust
756     /// # #![allow(unused)]
757     /// pub extern "C" fn str_type(p: &str) { }
758     /// ```
759     ///
760     /// {{produces}}
761     ///
762     /// ### Explanation
763     ///
764     /// There are many parameter and return types that may be specified in an
765     /// `extern` function that are not compatible with the given ABI. This
766     /// lint is an alert that these types should not be used. The lint usually
767     /// should provide a description of the issue, along with possibly a hint
768     /// on how to resolve it.
769     IMPROPER_CTYPES_DEFINITIONS,
770     Warn,
771     "proper use of libc types in foreign item definitions"
772 }
773 
774 declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS]);
775 
776 #[derive(Clone, Copy)]
777 pub(crate) enum CItemKind {
778     Declaration,
779     Definition,
780 }
781 
782 struct ImproperCTypesVisitor<'a, 'tcx> {
783     cx: &'a LateContext<'tcx>,
784     mode: CItemKind,
785 }
786 
787 enum FfiResult<'tcx> {
788     FfiSafe,
789     FfiPhantom(Ty<'tcx>),
790     FfiUnsafe { ty: Ty<'tcx>, reason: DiagnosticMessage, help: Option<DiagnosticMessage> },
791 }
792 
nonnull_optimization_guaranteed<'tcx>( tcx: TyCtxt<'tcx>, def: ty::AdtDef<'tcx>, ) -> bool793 pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
794     tcx: TyCtxt<'tcx>,
795     def: ty::AdtDef<'tcx>,
796 ) -> bool {
797     tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed)
798 }
799 
800 /// `repr(transparent)` structs can have a single non-ZST field, this function returns that
801 /// field.
transparent_newtype_field<'a, 'tcx>( tcx: TyCtxt<'tcx>, variant: &'a ty::VariantDef, ) -> Option<&'a ty::FieldDef>802 pub fn transparent_newtype_field<'a, 'tcx>(
803     tcx: TyCtxt<'tcx>,
804     variant: &'a ty::VariantDef,
805 ) -> Option<&'a ty::FieldDef> {
806     let param_env = tcx.param_env(variant.def_id);
807     variant.fields.iter().find(|field| {
808         let field_ty = tcx.type_of(field.did).subst_identity();
809         let is_zst = tcx.layout_of(param_env.and(field_ty)).is_ok_and(|layout| layout.is_zst());
810         !is_zst
811     })
812 }
813 
814 /// Is type known to be non-null?
ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool815 fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool {
816     let tcx = cx.tcx;
817     match ty.kind() {
818         ty::FnPtr(_) => true,
819         ty::Ref(..) => true,
820         ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true,
821         ty::Adt(def, substs) if def.repr().transparent() && !def.is_union() => {
822             let marked_non_null = nonnull_optimization_guaranteed(tcx, *def);
823 
824             if marked_non_null {
825                 return true;
826             }
827 
828             // `UnsafeCell` has its niche hidden.
829             if def.is_unsafe_cell() {
830                 return false;
831             }
832 
833             def.variants()
834                 .iter()
835                 .filter_map(|variant| transparent_newtype_field(cx.tcx, variant))
836                 .any(|field| ty_is_known_nonnull(cx, field.ty(tcx, substs), mode))
837         }
838         _ => false,
839     }
840 }
841 
842 /// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type.
843 /// If the type passed in was not scalar, returns None.
get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>>844 fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
845     let tcx = cx.tcx;
846     Some(match *ty.kind() {
847         ty::Adt(field_def, field_substs) => {
848             let inner_field_ty = {
849                 let mut first_non_zst_ty = field_def
850                     .variants()
851                     .iter()
852                     .filter_map(|v| transparent_newtype_field(cx.tcx, v));
853                 debug_assert_eq!(
854                     first_non_zst_ty.clone().count(),
855                     1,
856                     "Wrong number of fields for transparent type"
857                 );
858                 first_non_zst_ty
859                     .next_back()
860                     .expect("No non-zst fields in transparent type.")
861                     .ty(tcx, field_substs)
862             };
863             return get_nullable_type(cx, inner_field_ty);
864         }
865         ty::Int(ty) => Ty::new_int(tcx, ty),
866         ty::Uint(ty) => Ty::new_uint(tcx, ty),
867         ty::RawPtr(ty_mut) => Ty::new_ptr(tcx, ty_mut),
868         // As these types are always non-null, the nullable equivalent of
869         // Option<T> of these types are their raw pointer counterparts.
870         ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty::TypeAndMut { ty, mutbl }),
871         ty::FnPtr(..) => {
872             // There is no nullable equivalent for Rust's function pointers -- you
873             // must use an Option<fn(..) -> _> to represent it.
874             ty
875         }
876 
877         // We should only ever reach this case if ty_is_known_nonnull is extended
878         // to other types.
879         ref unhandled => {
880             debug!(
881                 "get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}",
882                 unhandled, ty
883             );
884             return None;
885         }
886     })
887 }
888 
889 /// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
890 /// can, return the type that `ty` can be safely converted to, otherwise return `None`.
891 /// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`,
892 /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes.
893 /// FIXME: This duplicates code in codegen.
repr_nullable_ptr<'tcx>( cx: &LateContext<'tcx>, ty: Ty<'tcx>, ckind: CItemKind, ) -> Option<Ty<'tcx>>894 pub(crate) fn repr_nullable_ptr<'tcx>(
895     cx: &LateContext<'tcx>,
896     ty: Ty<'tcx>,
897     ckind: CItemKind,
898 ) -> Option<Ty<'tcx>> {
899     debug!("is_repr_nullable_ptr(cx, ty = {:?})", ty);
900     if let ty::Adt(ty_def, substs) = ty.kind() {
901         let field_ty = match &ty_def.variants().raw[..] {
902             [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
903                 ([], [field]) | ([field], []) => field.ty(cx.tcx, substs),
904                 _ => return None,
905             },
906             _ => return None,
907         };
908 
909         if !ty_is_known_nonnull(cx, field_ty, ckind) {
910             return None;
911         }
912 
913         // At this point, the field's type is known to be nonnull and the parent enum is Option-like.
914         // If the computed size for the field and the enum are different, the nonnull optimization isn't
915         // being applied (and we've got a problem somewhere).
916         let compute_size_skeleton = |t| SizeSkeleton::compute(t, cx.tcx, cx.param_env).unwrap();
917         if !compute_size_skeleton(ty).same_size(compute_size_skeleton(field_ty)) {
918             bug!("improper_ctypes: Option nonnull optimization not applied?");
919         }
920 
921         // Return the nullable type this Option-like enum can be safely represented with.
922         let field_ty_abi = &cx.layout_of(field_ty).unwrap().abi;
923         if let Abi::Scalar(field_ty_scalar) = field_ty_abi {
924             match field_ty_scalar.valid_range(cx) {
925                 WrappingRange { start: 0, end }
926                     if end == field_ty_scalar.size(&cx.tcx).unsigned_int_max() - 1 =>
927                 {
928                     return Some(get_nullable_type(cx, field_ty).unwrap());
929                 }
930                 WrappingRange { start: 1, .. } => {
931                     return Some(get_nullable_type(cx, field_ty).unwrap());
932                 }
933                 WrappingRange { start, end } => {
934                     unreachable!("Unhandled start and end range: ({}, {})", start, end)
935                 }
936             };
937         }
938     }
939     None
940 }
941 
942 impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
943     /// Check if the type is array and emit an unsafe type lint.
check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool944     fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
945         if let ty::Array(..) = ty.kind() {
946             self.emit_ffi_unsafe_type_lint(
947                 ty,
948                 sp,
949                 fluent::lint_improper_ctypes_array_reason,
950                 Some(fluent::lint_improper_ctypes_array_help),
951             );
952             true
953         } else {
954             false
955         }
956     }
957 
958     /// Checks if the given field's type is "ffi-safe".
check_field_type_for_ffi( &self, cache: &mut FxHashSet<Ty<'tcx>>, field: &ty::FieldDef, substs: SubstsRef<'tcx>, ) -> FfiResult<'tcx>959     fn check_field_type_for_ffi(
960         &self,
961         cache: &mut FxHashSet<Ty<'tcx>>,
962         field: &ty::FieldDef,
963         substs: SubstsRef<'tcx>,
964     ) -> FfiResult<'tcx> {
965         let field_ty = field.ty(self.cx.tcx, substs);
966         let field_ty = self
967             .cx
968             .tcx
969             .try_normalize_erasing_regions(self.cx.param_env, field_ty)
970             .unwrap_or(field_ty);
971         self.check_type_for_ffi(cache, field_ty)
972     }
973 
974     /// Checks if the given `VariantDef`'s field types are "ffi-safe".
check_variant_for_ffi( &self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>, def: ty::AdtDef<'tcx>, variant: &ty::VariantDef, substs: SubstsRef<'tcx>, ) -> FfiResult<'tcx>975     fn check_variant_for_ffi(
976         &self,
977         cache: &mut FxHashSet<Ty<'tcx>>,
978         ty: Ty<'tcx>,
979         def: ty::AdtDef<'tcx>,
980         variant: &ty::VariantDef,
981         substs: SubstsRef<'tcx>,
982     ) -> FfiResult<'tcx> {
983         use FfiResult::*;
984 
985         let transparent_with_all_zst_fields = if def.repr().transparent() {
986             if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
987                 // Transparent newtypes have at most one non-ZST field which needs to be checked..
988                 match self.check_field_type_for_ffi(cache, field, substs) {
989                     FfiUnsafe { ty, .. } if ty.is_unit() => (),
990                     r => return r,
991                 }
992 
993                 false
994             } else {
995                 // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
996                 // `PhantomData`).
997                 true
998             }
999         } else {
1000             false
1001         };
1002 
1003         // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
1004         let mut all_phantom = !variant.fields.is_empty();
1005         for field in &variant.fields {
1006             all_phantom &= match self.check_field_type_for_ffi(cache, &field, substs) {
1007                 FfiSafe => false,
1008                 // `()` fields are FFI-safe!
1009                 FfiUnsafe { ty, .. } if ty.is_unit() => false,
1010                 FfiPhantom(..) => true,
1011                 r @ FfiUnsafe { .. } => return r,
1012             }
1013         }
1014 
1015         if all_phantom {
1016             FfiPhantom(ty)
1017         } else if transparent_with_all_zst_fields {
1018             FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
1019         } else {
1020             FfiSafe
1021         }
1022     }
1023 
1024     /// Checks if the given type is "ffi-safe" (has a stable, well-defined
1025     /// representation which can be exported to C code).
check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult<'tcx>1026     fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult<'tcx> {
1027         use FfiResult::*;
1028 
1029         let tcx = self.cx.tcx;
1030 
1031         // Protect against infinite recursion, for example
1032         // `struct S(*mut S);`.
1033         // FIXME: A recursion limit is necessary as well, for irregular
1034         // recursive types.
1035         if !cache.insert(ty) {
1036             return FfiSafe;
1037         }
1038 
1039         match *ty.kind() {
1040             ty::Adt(def, substs) => {
1041                 if def.is_box() && matches!(self.mode, CItemKind::Definition) {
1042                     if ty.boxed_ty().is_sized(tcx, self.cx.param_env) {
1043                         return FfiSafe;
1044                     } else {
1045                         return FfiUnsafe {
1046                             ty,
1047                             reason: fluent::lint_improper_ctypes_box,
1048                             help: None,
1049                         };
1050                     }
1051                 }
1052                 if def.is_phantom_data() {
1053                     return FfiPhantom(ty);
1054                 }
1055                 match def.adt_kind() {
1056                     AdtKind::Struct | AdtKind::Union => {
1057                         if !def.repr().c() && !def.repr().transparent() {
1058                             return FfiUnsafe {
1059                                 ty,
1060                                 reason: if def.is_struct() {
1061                                     fluent::lint_improper_ctypes_struct_layout_reason
1062                                 } else {
1063                                     fluent::lint_improper_ctypes_union_layout_reason
1064                                 },
1065                                 help: if def.is_struct() {
1066                                     Some(fluent::lint_improper_ctypes_struct_layout_help)
1067                                 } else {
1068                                     Some(fluent::lint_improper_ctypes_union_layout_help)
1069                                 },
1070                             };
1071                         }
1072 
1073                         let is_non_exhaustive =
1074                             def.non_enum_variant().is_field_list_non_exhaustive();
1075                         if is_non_exhaustive && !def.did().is_local() {
1076                             return FfiUnsafe {
1077                                 ty,
1078                                 reason: if def.is_struct() {
1079                                     fluent::lint_improper_ctypes_struct_non_exhaustive
1080                                 } else {
1081                                     fluent::lint_improper_ctypes_union_non_exhaustive
1082                                 },
1083                                 help: None,
1084                             };
1085                         }
1086 
1087                         if def.non_enum_variant().fields.is_empty() {
1088                             return FfiUnsafe {
1089                                 ty,
1090                                 reason: if def.is_struct() {
1091                                     fluent::lint_improper_ctypes_struct_fieldless_reason
1092                                 } else {
1093                                     fluent::lint_improper_ctypes_union_fieldless_reason
1094                                 },
1095                                 help: if def.is_struct() {
1096                                     Some(fluent::lint_improper_ctypes_struct_fieldless_help)
1097                                 } else {
1098                                     Some(fluent::lint_improper_ctypes_union_fieldless_help)
1099                                 },
1100                             };
1101                         }
1102 
1103                         self.check_variant_for_ffi(cache, ty, def, def.non_enum_variant(), substs)
1104                     }
1105                     AdtKind::Enum => {
1106                         if def.variants().is_empty() {
1107                             // Empty enums are okay... although sort of useless.
1108                             return FfiSafe;
1109                         }
1110 
1111                         // Check for a repr() attribute to specify the size of the
1112                         // discriminant.
1113                         if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none()
1114                         {
1115                             // Special-case types like `Option<extern fn()>`.
1116                             if repr_nullable_ptr(self.cx, ty, self.mode).is_none() {
1117                                 return FfiUnsafe {
1118                                     ty,
1119                                     reason: fluent::lint_improper_ctypes_enum_repr_reason,
1120                                     help: Some(fluent::lint_improper_ctypes_enum_repr_help),
1121                                 };
1122                             }
1123                         }
1124 
1125                         if def.is_variant_list_non_exhaustive() && !def.did().is_local() {
1126                             return FfiUnsafe {
1127                                 ty,
1128                                 reason: fluent::lint_improper_ctypes_non_exhaustive,
1129                                 help: None,
1130                             };
1131                         }
1132 
1133                         // Check the contained variants.
1134                         for variant in def.variants() {
1135                             let is_non_exhaustive = variant.is_field_list_non_exhaustive();
1136                             if is_non_exhaustive && !variant.def_id.is_local() {
1137                                 return FfiUnsafe {
1138                                     ty,
1139                                     reason: fluent::lint_improper_ctypes_non_exhaustive_variant,
1140                                     help: None,
1141                                 };
1142                             }
1143 
1144                             match self.check_variant_for_ffi(cache, ty, def, variant, substs) {
1145                                 FfiSafe => (),
1146                                 r => return r,
1147                             }
1148                         }
1149 
1150                         FfiSafe
1151                     }
1152                 }
1153             }
1154 
1155             ty::Char => FfiUnsafe {
1156                 ty,
1157                 reason: fluent::lint_improper_ctypes_char_reason,
1158                 help: Some(fluent::lint_improper_ctypes_char_help),
1159             },
1160 
1161             ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => {
1162                 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None }
1163             }
1164 
1165             // Primitive types with a stable representation.
1166             ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe,
1167 
1168             ty::Slice(_) => FfiUnsafe {
1169                 ty,
1170                 reason: fluent::lint_improper_ctypes_slice_reason,
1171                 help: Some(fluent::lint_improper_ctypes_slice_help),
1172             },
1173 
1174             ty::Dynamic(..) => {
1175                 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None }
1176             }
1177 
1178             ty::Str => FfiUnsafe {
1179                 ty,
1180                 reason: fluent::lint_improper_ctypes_str_reason,
1181                 help: Some(fluent::lint_improper_ctypes_str_help),
1182             },
1183 
1184             ty::Tuple(..) => FfiUnsafe {
1185                 ty,
1186                 reason: fluent::lint_improper_ctypes_tuple_reason,
1187                 help: Some(fluent::lint_improper_ctypes_tuple_help),
1188             },
1189 
1190             ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _)
1191                 if {
1192                     matches!(self.mode, CItemKind::Definition)
1193                         && ty.is_sized(self.cx.tcx, self.cx.param_env)
1194                 } =>
1195             {
1196                 FfiSafe
1197             }
1198 
1199             ty::RawPtr(ty::TypeAndMut { ty, .. })
1200                 if match ty.kind() {
1201                     ty::Tuple(tuple) => tuple.is_empty(),
1202                     _ => false,
1203                 } =>
1204             {
1205                 FfiSafe
1206             }
1207 
1208             ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
1209                 self.check_type_for_ffi(cache, ty)
1210             }
1211 
1212             ty::Array(inner_ty, _) => self.check_type_for_ffi(cache, inner_ty),
1213 
1214             ty::FnPtr(sig) => {
1215                 if self.is_internal_abi(sig.abi()) {
1216                     return FfiUnsafe {
1217                         ty,
1218                         reason: fluent::lint_improper_ctypes_fnptr_reason,
1219                         help: Some(fluent::lint_improper_ctypes_fnptr_help),
1220                     };
1221                 }
1222 
1223                 let sig = tcx.erase_late_bound_regions(sig);
1224                 for arg in sig.inputs() {
1225                     match self.check_type_for_ffi(cache, *arg) {
1226                         FfiSafe => {}
1227                         r => return r,
1228                     }
1229                 }
1230 
1231                 let ret_ty = sig.output();
1232                 if ret_ty.is_unit() {
1233                     return FfiSafe;
1234                 }
1235 
1236                 self.check_type_for_ffi(cache, ret_ty)
1237             }
1238 
1239             ty::Foreign(..) => FfiSafe,
1240 
1241             // While opaque types are checked for earlier, if a projection in a struct field
1242             // normalizes to an opaque type, then it will reach this branch.
1243             ty::Alias(ty::Opaque, ..) => {
1244                 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None }
1245             }
1246 
1247             // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
1248             //  so they are currently ignored for the purposes of this lint.
1249             ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
1250                 if matches!(self.mode, CItemKind::Definition) =>
1251             {
1252                 FfiSafe
1253             }
1254 
1255             ty::Param(..)
1256             | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
1257             | ty::Infer(..)
1258             | ty::Bound(..)
1259             | ty::Error(_)
1260             | ty::Closure(..)
1261             | ty::Generator(..)
1262             | ty::GeneratorWitness(..)
1263             | ty::GeneratorWitnessMIR(..)
1264             | ty::Placeholder(..)
1265             | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
1266         }
1267     }
1268 
emit_ffi_unsafe_type_lint( &mut self, ty: Ty<'tcx>, sp: Span, note: DiagnosticMessage, help: Option<DiagnosticMessage>, )1269     fn emit_ffi_unsafe_type_lint(
1270         &mut self,
1271         ty: Ty<'tcx>,
1272         sp: Span,
1273         note: DiagnosticMessage,
1274         help: Option<DiagnosticMessage>,
1275     ) {
1276         let lint = match self.mode {
1277             CItemKind::Declaration => IMPROPER_CTYPES,
1278             CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
1279         };
1280         let desc = match self.mode {
1281             CItemKind::Declaration => "block",
1282             CItemKind::Definition => "fn",
1283         };
1284         let span_note = if let ty::Adt(def, _) = ty.kind()
1285             && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) {
1286                 Some(sp)
1287             } else {
1288                 None
1289             };
1290         self.cx.emit_spanned_lint(
1291             lint,
1292             sp,
1293             ImproperCTypes { ty, desc, label: sp, help, note, span_note },
1294         );
1295     }
1296 
check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool1297     fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
1298         struct ProhibitOpaqueTypes;
1299         impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes {
1300             type BreakTy = Ty<'tcx>;
1301 
1302             fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
1303                 if !ty.has_opaque_types() {
1304                     return ControlFlow::Continue(());
1305                 }
1306 
1307                 if let ty::Alias(ty::Opaque, ..) = ty.kind() {
1308                     ControlFlow::Break(ty)
1309                 } else {
1310                     ty.super_visit_with(self)
1311                 }
1312             }
1313         }
1314 
1315         if let Some(ty) = self
1316             .cx
1317             .tcx
1318             .try_normalize_erasing_regions(self.cx.param_env, ty)
1319             .unwrap_or(ty)
1320             .visit_with(&mut ProhibitOpaqueTypes)
1321             .break_value()
1322         {
1323             self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None);
1324             true
1325         } else {
1326             false
1327         }
1328     }
1329 
check_type_for_ffi_and_report_errors( &mut self, sp: Span, ty: Ty<'tcx>, is_static: bool, is_return_type: bool, )1330     fn check_type_for_ffi_and_report_errors(
1331         &mut self,
1332         sp: Span,
1333         ty: Ty<'tcx>,
1334         is_static: bool,
1335         is_return_type: bool,
1336     ) {
1337         if self.check_for_opaque_ty(sp, ty) {
1338             // We've already emitted an error due to an opaque type.
1339             return;
1340         }
1341 
1342         let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.param_env, ty).unwrap_or(ty);
1343 
1344         // C doesn't really support passing arrays by value - the only way to pass an array by value
1345         // is through a struct. So, first test that the top level isn't an array, and then
1346         // recursively check the types inside.
1347         if !is_static && self.check_for_array_ty(sp, ty) {
1348             return;
1349         }
1350 
1351         // Don't report FFI errors for unit return types. This check exists here, and not in
1352         // the caller (where it would make more sense) so that normalization has definitely
1353         // happened.
1354         if is_return_type && ty.is_unit() {
1355             return;
1356         }
1357 
1358         match self.check_type_for_ffi(&mut FxHashSet::default(), ty) {
1359             FfiResult::FfiSafe => {}
1360             FfiResult::FfiPhantom(ty) => {
1361                 self.emit_ffi_unsafe_type_lint(
1362                     ty,
1363                     sp,
1364                     fluent::lint_improper_ctypes_only_phantomdata,
1365                     None,
1366                 );
1367             }
1368             FfiResult::FfiUnsafe { ty, reason, help } => {
1369                 self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
1370             }
1371         }
1372     }
1373 
1374     /// Check if a function's argument types and result type are "ffi-safe".
1375     ///
1376     /// For a external ABI function, argument types and the result type are walked to find fn-ptr
1377     /// types that have external ABIs, as these still need checked.
check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>)1378     fn check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
1379         let sig = self.cx.tcx.fn_sig(def_id).subst_identity();
1380         let sig = self.cx.tcx.erase_late_bound_regions(sig);
1381 
1382         for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1383             for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) {
1384                 self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, false);
1385             }
1386         }
1387 
1388         if let hir::FnRetTy::Return(ref ret_hir) = decl.output {
1389             for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(ret_hir, sig.output()) {
1390                 self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, true);
1391             }
1392         }
1393     }
1394 
1395     /// Check if a function's argument types and result type are "ffi-safe".
check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>)1396     fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
1397         let sig = self.cx.tcx.fn_sig(def_id).subst_identity();
1398         let sig = self.cx.tcx.erase_late_bound_regions(sig);
1399 
1400         for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1401             self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false);
1402         }
1403 
1404         if let hir::FnRetTy::Return(ref ret_hir) = decl.output {
1405             self.check_type_for_ffi_and_report_errors(ret_hir.span, sig.output(), false, true);
1406         }
1407     }
1408 
check_foreign_static(&mut self, id: hir::OwnerId, span: Span)1409     fn check_foreign_static(&mut self, id: hir::OwnerId, span: Span) {
1410         let ty = self.cx.tcx.type_of(id).subst_identity();
1411         self.check_type_for_ffi_and_report_errors(span, ty, true, false);
1412     }
1413 
is_internal_abi(&self, abi: SpecAbi) -> bool1414     fn is_internal_abi(&self, abi: SpecAbi) -> bool {
1415         matches!(
1416             abi,
1417             SpecAbi::Rust | SpecAbi::RustCall | SpecAbi::RustIntrinsic | SpecAbi::PlatformIntrinsic
1418         )
1419     }
1420 
1421     /// Find any fn-ptr types with external ABIs in `ty`.
1422     ///
1423     /// For example, `Option<extern "C" fn()>` returns `extern "C" fn()`
find_fn_ptr_ty_with_external_abi( &self, hir_ty: &hir::Ty<'tcx>, ty: Ty<'tcx>, ) -> Vec<(Ty<'tcx>, Span)>1424     fn find_fn_ptr_ty_with_external_abi(
1425         &self,
1426         hir_ty: &hir::Ty<'tcx>,
1427         ty: Ty<'tcx>,
1428     ) -> Vec<(Ty<'tcx>, Span)> {
1429         struct FnPtrFinder<'parent, 'a, 'tcx> {
1430             visitor: &'parent ImproperCTypesVisitor<'a, 'tcx>,
1431             spans: Vec<Span>,
1432             tys: Vec<Ty<'tcx>>,
1433         }
1434 
1435         impl<'parent, 'a, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'parent, 'a, 'tcx> {
1436             fn visit_ty(&mut self, ty: &'_ hir::Ty<'_>) {
1437                 debug!(?ty);
1438                 if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind
1439                     && !self.visitor.is_internal_abi(*abi)
1440                 {
1441                     self.spans.push(ty.span);
1442                 }
1443 
1444                 hir::intravisit::walk_ty(self, ty)
1445             }
1446         }
1447 
1448         impl<'vis, 'a, 'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'vis, 'a, 'tcx> {
1449             type BreakTy = Ty<'tcx>;
1450 
1451             fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
1452                 if let ty::FnPtr(sig) = ty.kind() && !self.visitor.is_internal_abi(sig.abi()) {
1453                     self.tys.push(ty);
1454                 }
1455 
1456                 ty.super_visit_with(self)
1457             }
1458         }
1459 
1460         let mut visitor = FnPtrFinder { visitor: &*self, spans: Vec::new(), tys: Vec::new() };
1461         ty.visit_with(&mut visitor);
1462         hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty);
1463 
1464         iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect()
1465     }
1466 }
1467 
1468 impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations {
check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>)1469     fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
1470         let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration };
1471         let abi = cx.tcx.hir().get_foreign_abi(it.hir_id());
1472 
1473         match it.kind {
1474             hir::ForeignItemKind::Fn(ref decl, _, _) if !vis.is_internal_abi(abi) => {
1475                 vis.check_foreign_fn(it.owner_id.def_id, decl);
1476             }
1477             hir::ForeignItemKind::Static(ref ty, _) if !vis.is_internal_abi(abi) => {
1478                 vis.check_foreign_static(it.owner_id, ty.span);
1479             }
1480             hir::ForeignItemKind::Fn(ref decl, _, _) => vis.check_fn(it.owner_id.def_id, decl),
1481             hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
1482         }
1483     }
1484 }
1485 
1486 impl ImproperCTypesDefinitions {
check_ty_maybe_containing_foreign_fnptr<'tcx>( &mut self, cx: &LateContext<'tcx>, hir_ty: &'tcx hir::Ty<'_>, ty: Ty<'tcx>, )1487     fn check_ty_maybe_containing_foreign_fnptr<'tcx>(
1488         &mut self,
1489         cx: &LateContext<'tcx>,
1490         hir_ty: &'tcx hir::Ty<'_>,
1491         ty: Ty<'tcx>,
1492     ) {
1493         let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
1494         for (fn_ptr_ty, span) in vis.find_fn_ptr_ty_with_external_abi(hir_ty, ty) {
1495             vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false);
1496         }
1497     }
1498 }
1499 
1500 /// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in
1501 /// `extern "C" { }` blocks):
1502 ///
1503 /// - `extern "<abi>" fn` definitions are checked in the same way as the
1504 ///   `ImproperCtypesDeclarations` visitor checks functions if `<abi>` is external (e.g. "C").
1505 /// - All other items which contain types (e.g. other functions, struct definitions, etc) are
1506 ///   checked for extern fn-ptrs with external ABIs.
1507 impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>)1508     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
1509         match item.kind {
1510             hir::ItemKind::Static(ty, ..)
1511             | hir::ItemKind::Const(ty, ..)
1512             | hir::ItemKind::TyAlias(ty, ..) => {
1513                 self.check_ty_maybe_containing_foreign_fnptr(
1514                     cx,
1515                     ty,
1516                     cx.tcx.type_of(item.owner_id).subst_identity(),
1517                 );
1518             }
1519             // See `check_fn`..
1520             hir::ItemKind::Fn(..) => {}
1521             // See `check_field_def`..
1522             hir::ItemKind::Union(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) => {}
1523             // Doesn't define something that can contain a external type to be checked.
1524             hir::ItemKind::Impl(..)
1525             | hir::ItemKind::TraitAlias(..)
1526             | hir::ItemKind::Trait(..)
1527             | hir::ItemKind::OpaqueTy(..)
1528             | hir::ItemKind::GlobalAsm(..)
1529             | hir::ItemKind::ForeignMod { .. }
1530             | hir::ItemKind::Mod(..)
1531             | hir::ItemKind::Macro(..)
1532             | hir::ItemKind::Use(..)
1533             | hir::ItemKind::ExternCrate(..) => {}
1534         }
1535     }
1536 
check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>)1537     fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
1538         self.check_ty_maybe_containing_foreign_fnptr(
1539             cx,
1540             field.ty,
1541             cx.tcx.type_of(field.def_id).subst_identity(),
1542         );
1543     }
1544 
check_fn( &mut self, cx: &LateContext<'tcx>, kind: hir::intravisit::FnKind<'tcx>, decl: &'tcx hir::FnDecl<'_>, _: &'tcx hir::Body<'_>, _: Span, id: LocalDefId, )1545     fn check_fn(
1546         &mut self,
1547         cx: &LateContext<'tcx>,
1548         kind: hir::intravisit::FnKind<'tcx>,
1549         decl: &'tcx hir::FnDecl<'_>,
1550         _: &'tcx hir::Body<'_>,
1551         _: Span,
1552         id: LocalDefId,
1553     ) {
1554         use hir::intravisit::FnKind;
1555 
1556         let abi = match kind {
1557             FnKind::ItemFn(_, _, header, ..) => header.abi,
1558             FnKind::Method(_, sig, ..) => sig.header.abi,
1559             _ => return,
1560         };
1561 
1562         let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
1563         if vis.is_internal_abi(abi) {
1564             vis.check_fn(id, decl);
1565         } else {
1566             vis.check_foreign_fn(id, decl);
1567         }
1568     }
1569 }
1570 
1571 declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]);
1572 
1573 impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>)1574     fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
1575         if let hir::ItemKind::Enum(ref enum_definition, _) = it.kind {
1576             let t = cx.tcx.type_of(it.owner_id).subst_identity();
1577             let ty = cx.tcx.erase_regions(t);
1578             let Ok(layout) = cx.layout_of(ty) else { return };
1579             let Variants::Multiple {
1580                     tag_encoding: TagEncoding::Direct, tag, ref variants, ..
1581                 } = &layout.variants else {
1582                 return
1583             };
1584 
1585             let tag_size = tag.size(&cx.tcx).bytes();
1586 
1587             debug!(
1588                 "enum `{}` is {} bytes large with layout:\n{:#?}",
1589                 t,
1590                 layout.size.bytes(),
1591                 layout
1592             );
1593 
1594             let (largest, slargest, largest_index) = iter::zip(enum_definition.variants, variants)
1595                 .map(|(variant, variant_layout)| {
1596                     // Subtract the size of the enum tag.
1597                     let bytes = variant_layout.size.bytes().saturating_sub(tag_size);
1598 
1599                     debug!("- variant `{}` is {} bytes large", variant.ident, bytes);
1600                     bytes
1601                 })
1602                 .enumerate()
1603                 .fold((0, 0, 0), |(l, s, li), (idx, size)| {
1604                     if size > l {
1605                         (size, l, idx)
1606                     } else if size > s {
1607                         (l, size, li)
1608                     } else {
1609                         (l, s, li)
1610                     }
1611                 });
1612 
1613             // We only warn if the largest variant is at least thrice as large as
1614             // the second-largest.
1615             if largest > slargest * 3 && slargest > 0 {
1616                 cx.emit_spanned_lint(
1617                     VARIANT_SIZE_DIFFERENCES,
1618                     enum_definition.variants[largest_index].span,
1619                     VariantSizeDifferencesDiag { largest },
1620                 );
1621             }
1622         }
1623     }
1624 }
1625 
1626 declare_lint! {
1627     /// The `invalid_atomic_ordering` lint detects passing an `Ordering`
1628     /// to an atomic operation that does not support that ordering.
1629     ///
1630     /// ### Example
1631     ///
1632     /// ```rust,compile_fail
1633     /// # use core::sync::atomic::{AtomicU8, Ordering};
1634     /// let atom = AtomicU8::new(0);
1635     /// let value = atom.load(Ordering::Release);
1636     /// # let _ = value;
1637     /// ```
1638     ///
1639     /// {{produces}}
1640     ///
1641     /// ### Explanation
1642     ///
1643     /// Some atomic operations are only supported for a subset of the
1644     /// `atomic::Ordering` variants. Passing an unsupported variant will cause
1645     /// an unconditional panic at runtime, which is detected by this lint.
1646     ///
1647     /// This lint will trigger in the following cases: (where `AtomicType` is an
1648     /// atomic type from `core::sync::atomic`, such as `AtomicBool`,
1649     /// `AtomicPtr`, `AtomicUsize`, or any of the other integer atomics).
1650     ///
1651     /// - Passing `Ordering::Acquire` or `Ordering::AcqRel` to
1652     ///   `AtomicType::store`.
1653     ///
1654     /// - Passing `Ordering::Release` or `Ordering::AcqRel` to
1655     ///   `AtomicType::load`.
1656     ///
1657     /// - Passing `Ordering::Relaxed` to `core::sync::atomic::fence` or
1658     ///   `core::sync::atomic::compiler_fence`.
1659     ///
1660     /// - Passing `Ordering::Release` or `Ordering::AcqRel` as the failure
1661     ///   ordering for any of `AtomicType::compare_exchange`,
1662     ///   `AtomicType::compare_exchange_weak`, or `AtomicType::fetch_update`.
1663     INVALID_ATOMIC_ORDERING,
1664     Deny,
1665     "usage of invalid atomic ordering in atomic operations and memory fences"
1666 }
1667 
1668 declare_lint_pass!(InvalidAtomicOrdering => [INVALID_ATOMIC_ORDERING]);
1669 
1670 impl InvalidAtomicOrdering {
inherent_atomic_method_call<'hir>( cx: &LateContext<'_>, expr: &Expr<'hir>, recognized_names: &[Symbol], ) -> Option<(Symbol, &'hir [Expr<'hir>])>1671     fn inherent_atomic_method_call<'hir>(
1672         cx: &LateContext<'_>,
1673         expr: &Expr<'hir>,
1674         recognized_names: &[Symbol], // used for fast path calculation
1675     ) -> Option<(Symbol, &'hir [Expr<'hir>])> {
1676         const ATOMIC_TYPES: &[Symbol] = &[
1677             sym::AtomicBool,
1678             sym::AtomicPtr,
1679             sym::AtomicUsize,
1680             sym::AtomicU8,
1681             sym::AtomicU16,
1682             sym::AtomicU32,
1683             sym::AtomicU64,
1684             sym::AtomicU128,
1685             sym::AtomicIsize,
1686             sym::AtomicI8,
1687             sym::AtomicI16,
1688             sym::AtomicI32,
1689             sym::AtomicI64,
1690             sym::AtomicI128,
1691         ];
1692         if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind
1693             && recognized_names.contains(&method_path.ident.name)
1694             && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
1695             && let Some(impl_did) = cx.tcx.impl_of_method(m_def_id)
1696             && let Some(adt) = cx.tcx.type_of(impl_did).subst_identity().ty_adt_def()
1697             // skip extension traits, only lint functions from the standard library
1698             && cx.tcx.trait_id_of_impl(impl_did).is_none()
1699             && let parent = cx.tcx.parent(adt.did())
1700             && cx.tcx.is_diagnostic_item(sym::atomic_mod, parent)
1701             && ATOMIC_TYPES.contains(&cx.tcx.item_name(adt.did()))
1702         {
1703             return Some((method_path.ident.name, args));
1704         }
1705         None
1706     }
1707 
match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol>1708     fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol> {
1709         let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None };
1710         let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?;
1711         let tcx = cx.tcx;
1712         let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering);
1713         let name = tcx.item_name(did);
1714         let parent = tcx.parent(did);
1715         [sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find(
1716             |&ordering| {
1717                 name == ordering
1718                     && (Some(parent) == atomic_ordering
1719                             // needed in case this is a ctor, not a variant
1720                             || tcx.opt_parent(parent) == atomic_ordering)
1721             },
1722         )
1723     }
1724 
check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>)1725     fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
1726         if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
1727             && let Some((ordering_arg, invalid_ordering)) = match method {
1728                 sym::load => Some((&args[0], sym::Release)),
1729                 sym::store => Some((&args[1], sym::Acquire)),
1730                 _ => None,
1731             }
1732             && let Some(ordering) = Self::match_ordering(cx, ordering_arg)
1733             && (ordering == invalid_ordering || ordering == sym::AcqRel)
1734         {
1735             if method == sym::load {
1736                 cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad);
1737             } else {
1738                 cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore);
1739             };
1740         }
1741     }
1742 
check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>)1743     fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
1744         if let ExprKind::Call(ref func, ref args) = expr.kind
1745             && let ExprKind::Path(ref func_qpath) = func.kind
1746             && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
1747             && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
1748             && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
1749         {
1750             cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence);
1751         }
1752     }
1753 
check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>)1754     fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
1755         let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak])
1756             else {return };
1757 
1758         let fail_order_arg = match method {
1759             sym::fetch_update => &args[1],
1760             sym::compare_exchange | sym::compare_exchange_weak => &args[3],
1761             _ => return,
1762         };
1763 
1764         let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
1765 
1766         if matches!(fail_ordering, sym::Release | sym::AcqRel) {
1767             cx.emit_spanned_lint(
1768                 INVALID_ATOMIC_ORDERING,
1769                 fail_order_arg.span,
1770                 InvalidAtomicOrderingDiag { method, fail_order_arg_span: fail_order_arg.span },
1771             );
1772         }
1773     }
1774 }
1775 
1776 impl<'tcx> LateLintPass<'tcx> for InvalidAtomicOrdering {
check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)1777     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1778         Self::check_atomic_load_store(cx, expr);
1779         Self::check_memory_fence(cx, expr);
1780         Self::check_atomic_compare_exchange(cx, expr);
1781     }
1782 }
1783