]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_lint/src/types.rs
lint: port atomic ordering diagnostics
[rust.git] / compiler / rustc_lint / src / types.rs
index 2a2dc6822ce16aff30e7452e5d73d1b6026fc7f0..0056872ee44c30e27cebaffa46fc80a081991329 100644 (file)
@@ -2,9 +2,8 @@
 use rustc_ast as ast;
 use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::Applicability;
+use rustc_errors::{fluent, Applicability, DiagnosticMessage};
 use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
 use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
 use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
 use rustc_middle::ty::subst::SubstsRef;
@@ -140,7 +139,8 @@ fn lint_overflowing_range_endpoint<'tcx>(
         // overflowing and only by 1.
         if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max {
             cx.struct_span_lint(OVERFLOWING_LITERALS, parent_expr.span, |lint| {
-                let mut err = lint.build(&format!("range endpoint is out of range for `{}`", ty));
+                let mut err = lint.build(fluent::lint::range_endpoint_out_of_range);
+                err.set_arg("ty", ty);
                 if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) {
                     use ast::{LitIntType, LitKind};
                     // We need to preserve the literal's suffix,
@@ -154,7 +154,7 @@ fn lint_overflowing_range_endpoint<'tcx>(
                     let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix);
                     err.span_suggestion(
                         parent_expr.span,
-                        "use an inclusive range instead",
+                        fluent::lint::suggestion,
                         suggestion,
                         Applicability::MachineApplicable,
                     );
@@ -230,38 +230,35 @@ fn report_bin_hex_error(
                 (t.name_str(), actually.to_string())
             }
         };
-        let mut err = lint.build(&format!("literal out of range for `{}`", t));
+        let mut err = lint.build(fluent::lint::overflowing_bin_hex);
         if negative {
             // If the value is negative,
             // emits a note about the value itself, apart from the literal.
-            err.note(&format!(
-                "the literal `{}` (decimal `{}`) does not fit into \
-                 the type `{}`",
-                repr_str, val, t
-            ));
-            err.note(&format!("and the value `-{}` will become `{}{}`", repr_str, actually, t));
+            err.note(fluent::lint::negative_note);
+            err.note(fluent::lint::negative_becomes_note);
         } else {
-            err.note(&format!(
-                "the literal `{}` (decimal `{}`) does not fit into \
-                 the type `{}` and will become `{}{}`",
-                repr_str, val, t, actually, t
-            ));
+            err.note(fluent::lint::positive_note);
         }
         if let Some(sugg_ty) =
             get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative)
         {
+            err.set_arg("suggestion_ty", sugg_ty);
             if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
                 let (sans_suffix, _) = repr_str.split_at(pos);
                 err.span_suggestion(
                     expr.span,
-                    &format!("consider using the type `{}` instead", sugg_ty),
+                    fluent::lint::suggestion,
                     format!("{}{}", sans_suffix, sugg_ty),
                     Applicability::MachineApplicable,
                 );
             } else {
-                err.help(&format!("consider using the type `{}` instead", sugg_ty));
+                err.help(fluent::lint::help);
             }
         }
+        err.set_arg("ty", t);
+        err.set_arg("lit", repr_str);
+        err.set_arg("dec", val);
+        err.set_arg("actually", actually);
         err.emit();
     });
 }
@@ -354,21 +351,23 @@ fn lint_int_literal<'tcx>(
         }
 
         cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| {
-            let mut err = lint.build(&format!("literal out of range for `{}`", t.name_str()));
-            err.note(&format!(
-                "the literal `{}` does not fit into the type `{}` whose range is `{}..={}`",
+            let mut err = lint.build(fluent::lint::overflowing_int);
+            err.set_arg("ty", t.name_str());
+            err.set_arg(
+                "lit",
                 cx.sess()
                     .source_map()
                     .span_to_snippet(lit.span)
                     .expect("must get snippet from literal"),
-                t.name_str(),
-                min,
-                max,
-            ));
+            );
+            err.set_arg("min", min);
+            err.set_arg("max", max);
+            err.note(fluent::lint::note);
             if let Some(sugg_ty) =
                 get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
             {
-                err.help(&format!("consider using the type `{}` instead", sugg_ty));
+                err.set_arg("suggestion_ty", sugg_ty);
+                err.help(fluent::lint::help);
             }
             err.emit();
         });
@@ -396,10 +395,10 @@ fn lint_uint_literal<'tcx>(
                 hir::ExprKind::Cast(..) => {
                     if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() {
                         cx.struct_span_lint(OVERFLOWING_LITERALS, par_e.span, |lint| {
-                            lint.build("only `u8` can be cast into `char`")
+                            lint.build(fluent::lint::only_cast_u8_to_char)
                                 .span_suggestion(
                                     par_e.span,
-                                    "use a `char` literal instead",
+                                    fluent::lint::suggestion,
                                     format!("'\\u{{{:X}}}'", lit_val),
                                     Applicability::MachineApplicable,
                                 )
@@ -430,17 +429,18 @@ fn lint_uint_literal<'tcx>(
             return;
         }
         cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| {
-            lint.build(&format!("literal out of range for `{}`", t.name_str()))
-                .note(&format!(
-                    "the literal `{}` does not fit into the type `{}` whose range is `{}..={}`",
+            lint.build(fluent::lint::overflowing_uint)
+                .set_arg("ty", t.name_str())
+                .set_arg(
+                    "lit",
                     cx.sess()
                         .source_map()
                         .span_to_snippet(lit.span)
                         .expect("must get snippet from literal"),
-                    t.name_str(),
-                    min,
-                    max,
-                ))
+                )
+                .set_arg("min", min)
+                .set_arg("max", max)
+                .note(fluent::lint::note)
                 .emit();
         });
     }
@@ -472,16 +472,16 @@ fn lint_literal<'tcx>(
             };
             if is_infinite == Ok(true) {
                 cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| {
-                    lint.build(&format!("literal out of range for `{}`", t.name_str()))
-                        .note(&format!(
-                            "the literal `{}` does not fit into the type `{}` and will be converted to `{}::INFINITY`",
+                    lint.build(fluent::lint::overflowing_literal)
+                        .set_arg("ty", t.name_str())
+                        .set_arg(
+                            "lit",
                             cx.sess()
                                 .source_map()
                                 .span_to_snippet(lit.span)
                                 .expect("must get snippet from literal"),
-                            t.name_str(),
-                            t.name_str(),
-                        ))
+                        )
+                        .note(fluent::lint::note)
                         .emit();
                 });
             }
@@ -502,7 +502,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
             hir::ExprKind::Binary(binop, ref l, ref r) => {
                 if is_comparison(binop) && !check_limits(cx, binop, &l, &r) {
                     cx.struct_span_lint(UNUSED_COMPARISONS, e.span, |lint| {
-                        lint.build("comparison is useless due to type limits").emit();
+                        lint.build(fluent::lint::unused_comparisons).emit();
                     });
                 }
             }
@@ -664,7 +664,7 @@ struct ImproperCTypesVisitor<'a, 'tcx> {
 enum FfiResult<'tcx> {
     FfiSafe,
     FfiPhantom(Ty<'tcx>),
-    FfiUnsafe { ty: Ty<'tcx>, reason: String, help: Option<String> },
+    FfiUnsafe { ty: Ty<'tcx>, reason: DiagnosticMessage, help: Option<DiagnosticMessage> },
 }
 
 pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
@@ -824,8 +824,8 @@ fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
             self.emit_ffi_unsafe_type_lint(
                 ty,
                 sp,
-                "passing raw arrays by value is not FFI-safe",
-                Some("consider passing a pointer to the array"),
+                fluent::lint::improper_ctypes_array_reason,
+                Some(fluent::lint::improper_ctypes_array_help),
             );
             true
         } else {
@@ -868,11 +868,7 @@ fn check_variant_for_ffi(
             } else {
                 // All fields are ZSTs; this means that the type should behave
                 // like (), which is FFI-unsafe
-                FfiUnsafe {
-                    ty,
-                    reason: "this struct contains only zero-sized fields".into(),
-                    help: None,
-                }
+                FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_struct_zst, help: None }
             }
         } else {
             // We can't completely trust repr(C) markings; make sure the fields are
@@ -886,7 +882,7 @@ fn check_variant_for_ffi(
                     FfiPhantom(..) if def.is_enum() => {
                         return FfiUnsafe {
                             ty,
-                            reason: "this enum contains a PhantomData field".into(),
+                            reason: fluent::lint::improper_ctypes_enum_phantomdata,
                             help: None,
                         };
                     }
@@ -922,7 +918,7 @@ fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> F
                     } else {
                         return FfiUnsafe {
                             ty,
-                            reason: "box cannot be represented as a single pointer".to_string(),
+                            reason: fluent::lint::improper_ctypes_box,
                             help: None,
                         };
                     }
@@ -932,17 +928,19 @@ fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> F
                 }
                 match def.adt_kind() {
                     AdtKind::Struct | AdtKind::Union => {
-                        let kind = if def.is_struct() { "struct" } else { "union" };
-
                         if !def.repr().c() && !def.repr().transparent() {
                             return FfiUnsafe {
                                 ty,
-                                reason: format!("this {} has unspecified layout", kind),
-                                help: Some(format!(
-                                    "consider adding a `#[repr(C)]` or \
-                                             `#[repr(transparent)]` attribute to this {}",
-                                    kind
-                                )),
+                                reason: if def.is_struct() {
+                                    fluent::lint::improper_ctypes_struct_layout_reason
+                                } else {
+                                    fluent::lint::improper_ctypes_union_layout_reason
+                                },
+                                help: if def.is_struct() {
+                                    Some(fluent::lint::improper_ctypes_struct_layout_help)
+                                } else {
+                                    Some(fluent::lint::improper_ctypes_union_layout_help)
+                                },
                             };
                         }
 
@@ -951,7 +949,11 @@ fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> F
                         if is_non_exhaustive && !def.did().is_local() {
                             return FfiUnsafe {
                                 ty,
-                                reason: format!("this {} is non-exhaustive", kind),
+                                reason: if def.is_struct() {
+                                    fluent::lint::improper_ctypes_struct_non_exhaustive
+                                } else {
+                                    fluent::lint::improper_ctypes_union_non_exhaustive
+                                },
                                 help: None,
                             };
                         }
@@ -959,8 +961,16 @@ fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> F
                         if def.non_enum_variant().fields.is_empty() {
                             return FfiUnsafe {
                                 ty,
-                                reason: format!("this {} has no fields", kind),
-                                help: Some(format!("consider adding a member to this {}", kind)),
+                                reason: if def.is_struct() {
+                                    fluent::lint::improper_ctypes_struct_fieldless_reason
+                                } else {
+                                    fluent::lint::improper_ctypes_union_fieldless_reason
+                                },
+                                help: if def.is_struct() {
+                                    Some(fluent::lint::improper_ctypes_struct_fieldless_help)
+                                } else {
+                                    Some(fluent::lint::improper_ctypes_union_fieldless_help)
+                                },
                             };
                         }
 
@@ -980,13 +990,8 @@ fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> F
                             if repr_nullable_ptr(self.cx, ty, self.mode).is_none() {
                                 return FfiUnsafe {
                                     ty,
-                                    reason: "enum has no representation hint".into(),
-                                    help: Some(
-                                        "consider adding a `#[repr(C)]`, \
-                                                `#[repr(transparent)]`, or integer `#[repr(...)]` \
-                                                attribute to this enum"
-                                            .into(),
-                                    ),
+                                    reason: fluent::lint::improper_ctypes_enum_repr_reason,
+                                    help: Some(fluent::lint::improper_ctypes_enum_repr_help),
                                 };
                             }
                         }
@@ -994,7 +999,7 @@ fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> F
                         if def.is_variant_list_non_exhaustive() && !def.did().is_local() {
                             return FfiUnsafe {
                                 ty,
-                                reason: "this enum is non-exhaustive".into(),
+                                reason: fluent::lint::improper_ctypes_non_exhaustive,
                                 help: None,
                             };
                         }
@@ -1005,7 +1010,7 @@ fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> F
                             if is_non_exhaustive && !variant.def_id.is_local() {
                                 return FfiUnsafe {
                                     ty,
-                                    reason: "this enum has non-exhaustive variants".into(),
+                                    reason: fluent::lint::improper_ctypes_non_exhaustive_variant,
                                     help: None,
                                 };
                             }
@@ -1023,39 +1028,37 @@ fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> F
 
             ty::Char => FfiUnsafe {
                 ty,
-                reason: "the `char` type has no C equivalent".into(),
-                help: Some("consider using `u32` or `libc::wchar_t` instead".into()),
+                reason: fluent::lint::improper_ctypes_char_reason,
+                help: Some(fluent::lint::improper_ctypes_char_help),
             },
 
-            ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => FfiUnsafe {
-                ty,
-                reason: "128-bit integers don't currently have a known stable ABI".into(),
-                help: None,
-            },
+            ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => {
+                FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_128bit, help: None }
+            }
 
             // Primitive types with a stable representation.
             ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe,
 
             ty::Slice(_) => FfiUnsafe {
                 ty,
-                reason: "slices have no C equivalent".into(),
-                help: Some("consider using a raw pointer instead".into()),
+                reason: fluent::lint::improper_ctypes_slice_reason,
+                help: Some(fluent::lint::improper_ctypes_slice_help),
             },
 
             ty::Dynamic(..) => {
-                FfiUnsafe { ty, reason: "trait objects have no C equivalent".into(), help: None }
+                FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_dyn, help: None }
             }
 
             ty::Str => FfiUnsafe {
                 ty,
-                reason: "string slices have no C equivalent".into(),
-                help: Some("consider using `*const u8` and a length instead".into()),
+                reason: fluent::lint::improper_ctypes_str_reason,
+                help: Some(fluent::lint::improper_ctypes_str_help),
             },
 
             ty::Tuple(..) => FfiUnsafe {
                 ty,
-                reason: "tuples have unspecified layout".into(),
-                help: Some("consider using a struct instead".into()),
+                reason: fluent::lint::improper_ctypes_tuple_reason,
+                help: Some(fluent::lint::improper_ctypes_tuple_help),
             },
 
             ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _)
@@ -1086,12 +1089,8 @@ fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> F
                 if self.is_internal_abi(sig.abi()) {
                     return FfiUnsafe {
                         ty,
-                        reason: "this function pointer has Rust-specific calling convention".into(),
-                        help: Some(
-                            "consider using an `extern fn(...) -> ...` \
-                                    function pointer instead"
-                                .into(),
-                        ),
+                        reason: fluent::lint::improper_ctypes_fnptr_reason,
+                        help: Some(fluent::lint::improper_ctypes_fnptr_help),
                     };
                 }
 
@@ -1122,7 +1121,7 @@ fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> F
             // While opaque types are checked for earlier, if a projection in a struct field
             // normalizes to an opaque type, then it will reach this branch.
             ty::Opaque(..) => {
-                FfiUnsafe { ty, reason: "opaque types have no C equivalent".into(), help: None }
+                FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_opaque, help: None }
             }
 
             // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
@@ -1148,8 +1147,8 @@ fn emit_ffi_unsafe_type_lint(
         &mut self,
         ty: Ty<'tcx>,
         sp: Span,
-        note: &str,
-        help: Option<&str>,
+        note: DiagnosticMessage,
+        help: Option<DiagnosticMessage>,
     ) {
         let lint = match self.mode {
             CItemKind::Declaration => IMPROPER_CTYPES,
@@ -1161,18 +1160,17 @@ fn emit_ffi_unsafe_type_lint(
                 CItemKind::Declaration => "block",
                 CItemKind::Definition => "fn",
             };
-            let mut diag = lint.build(&format!(
-                "`extern` {} uses type `{}`, which is not FFI-safe",
-                item_description, ty
-            ));
-            diag.span_label(sp, "not FFI-safe");
+            let mut diag = lint.build(fluent::lint::improper_ctypes);
+            diag.set_arg("ty", ty);
+            diag.set_arg("desc", item_description);
+            diag.span_label(sp, fluent::lint::label);
             if let Some(help) = help {
                 diag.help(help);
             }
             diag.note(note);
             if let ty::Adt(def, _) = ty.kind() {
                 if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) {
-                    diag.span_note(sp, "the type is defined here");
+                    diag.span_note(sp, fluent::lint::note);
                 }
             }
             diag.emit();
@@ -1209,7 +1207,7 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         }
 
         if let Some(ty) = ty.visit_with(&mut ProhibitOpaqueTypes { cx: self.cx }).break_value() {
-            self.emit_ffi_unsafe_type_lint(ty, sp, "opaque types have no C equivalent", None);
+            self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint::improper_ctypes_opaque, None);
             true
         } else {
             false
@@ -1251,13 +1249,18 @@ fn check_type_for_ffi_and_report_errors(
         match self.check_type_for_ffi(&mut FxHashSet::default(), ty) {
             FfiResult::FfiSafe => {}
             FfiResult::FfiPhantom(ty) => {
-                self.emit_ffi_unsafe_type_lint(ty, sp, "composed only of `PhantomData`", None);
+                self.emit_ffi_unsafe_type_lint(
+                    ty,
+                    sp,
+                    fluent::lint::improper_ctypes_only_phantomdata,
+                    None,
+                );
             }
             // If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic
             // argument, which after substitution, is `()`, then this branch can be hit.
             FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => {}
             FfiResult::FfiUnsafe { ty, reason, help } => {
-                self.emit_ffi_unsafe_type_lint(ty, sp, &reason, help.as_deref());
+                self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
             }
         }
     }
@@ -1384,12 +1387,9 @@ fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
                     VARIANT_SIZE_DIFFERENCES,
                     enum_definition.variants[largest_index].span,
                     |lint| {
-                        lint.build(&format!(
-                            "enum variant is more than three times \
-                                          larger ({} bytes) than the next largest",
-                            largest
-                        ))
-                        .emit();
+                        lint.build(fluent::lint::variant_size_differences)
+                            .set_arg("largest", largest)
+                            .emit();
                     },
                 );
             }
@@ -1483,49 +1483,42 @@ fn inherent_atomic_method_call<'hir>(
         None
     }
 
-    fn matches_ordering(cx: &LateContext<'_>, did: DefId, orderings: &[Symbol]) -> bool {
+    fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol> {
+        let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None };
+        let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?;
         let tcx = cx.tcx;
         let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering);
-        orderings.iter().any(|ordering| {
-            tcx.item_name(did) == *ordering && {
-                let parent = tcx.parent(did);
-                Some(parent) == atomic_ordering
-                    // needed in case this is a ctor, not a variant
-                    || tcx.opt_parent(parent) == atomic_ordering
-            }
-        })
-    }
-
-    fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<DefId> {
-        if let ExprKind::Path(ref ord_qpath) = ord_arg.kind {
-            cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()
-        } else {
-            None
-        }
+        let name = tcx.item_name(did);
+        let parent = tcx.parent(did);
+        [sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find(
+            |&ordering| {
+                name == ordering
+                    && (Some(parent) == atomic_ordering
+                            // needed in case this is a ctor, not a variant
+                            || tcx.opt_parent(parent) == atomic_ordering)
+            },
+        )
     }
 
     fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
-        use rustc_hir::def::{DefKind, Res};
-        use rustc_hir::QPath;
         if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
             && let Some((ordering_arg, invalid_ordering)) = match method {
                 sym::load => Some((&args[1], sym::Release)),
                 sym::store => Some((&args[2], sym::Acquire)),
                 _ => None,
             }
-            && let ExprKind::Path(QPath::Resolved(_, path)) = ordering_arg.kind
-            && let Res::Def(DefKind::Ctor(..), ctor_id) = path.res
-            && Self::matches_ordering(cx, ctor_id, &[invalid_ordering, sym::AcqRel])
+            && let Some(ordering) = Self::match_ordering(cx, ordering_arg)
+            && (ordering == invalid_ordering || ordering == sym::AcqRel)
         {
             cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, |diag| {
                 if method == sym::load {
-                    diag.build("atomic loads cannot have `Release` or `AcqRel` ordering")
-                        .help("consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`")
+                    diag.build(fluent::lint::atomic_ordering_load)
+                        .help(fluent::lint::help)
                         .emit()
                 } else {
                     debug_assert_eq!(method, sym::store);
-                    diag.build("atomic stores cannot have `Acquire` or `AcqRel` ordering")
-                        .help("consider using ordering modes `Release`, `SeqCst` or `Relaxed`")
+                    diag.build(fluent::lint::atomic_ordering_store)
+                        .help(fluent::lint::help)
                         .emit();
                 }
             });
@@ -1537,75 +1530,67 @@ fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
             && let ExprKind::Path(ref func_qpath) = func.kind
             && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
             && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
-            && let ExprKind::Path(ref ordering_qpath) = &args[0].kind
-            && let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id()
-            && Self::matches_ordering(cx, ordering_def_id, &[sym::Relaxed])
+            && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
         {
             cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, |diag| {
-                diag.build("memory fences cannot have `Relaxed` ordering")
-                    .help("consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`")
+                diag.build(fluent::lint::atomic_ordering_fence)
+                    .help(fluent::lint::help)
                     .emit();
             });
         }
     }
 
     fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak])
-            && let Some((success_order_arg, failure_order_arg)) = match method {
-                sym::fetch_update => Some((&args[1], &args[2])),
-                sym::compare_exchange | sym::compare_exchange_weak => Some((&args[3], &args[4])),
-                _ => None,
-            }
-            && let Some(fail_ordering_def_id) = Self::opt_ordering_defid(cx, failure_order_arg)
-        {
-            // Helper type holding on to some checking and error reporting data. Has
-            // - (success ordering,
-            // - list of failure orderings forbidden by the success order,
-            // - suggestion message)
-            type OrdLintInfo = (Symbol, &'static [Symbol], &'static str);
-            const RELAXED: OrdLintInfo = (sym::Relaxed, &[sym::SeqCst, sym::Acquire], "ordering mode `Relaxed`");
-            const ACQUIRE: OrdLintInfo = (sym::Acquire, &[sym::SeqCst], "ordering modes `Acquire` or `Relaxed`");
-            const SEQ_CST: OrdLintInfo = (sym::SeqCst, &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`");
-            const RELEASE: OrdLintInfo = (sym::Release, RELAXED.1, RELAXED.2);
-            const ACQREL: OrdLintInfo = (sym::AcqRel, ACQUIRE.1, ACQUIRE.2);
-            const SEARCH: [OrdLintInfo; 5] = [RELAXED, ACQUIRE, SEQ_CST, RELEASE, ACQREL];
-
-            let success_lint_info = Self::opt_ordering_defid(cx, success_order_arg)
-                .and_then(|success_ord_def_id| -> Option<OrdLintInfo> {
-                    SEARCH
-                        .iter()
-                        .copied()
-                        .find(|(ordering, ..)| {
-                            Self::matches_ordering(cx, success_ord_def_id, &[*ordering])
-                        })
-                });
-            if Self::matches_ordering(cx, fail_ordering_def_id, &[sym::Release, sym::AcqRel]) {
-                // If we don't know the success order is, use what we'd suggest
-                // if it were maximally permissive.
-                let suggested = success_lint_info.unwrap_or(SEQ_CST).2;
-                cx.struct_span_lint(INVALID_ATOMIC_ORDERING, failure_order_arg.span, |diag| {
-                    let msg = format!(
-                        "{}'s failure ordering may not be `Release` or `AcqRel`",
-                        method,
-                    );
-                    diag.build(&msg)
-                        .help(&format!("consider using {} instead", suggested))
-                        .emit();
-                });
-            } else if let Some((success_ord, bad_ords_given_success, suggested)) = success_lint_info {
-                if Self::matches_ordering(cx, fail_ordering_def_id, bad_ords_given_success) {
-                    cx.struct_span_lint(INVALID_ATOMIC_ORDERING, failure_order_arg.span, |diag| {
-                        let msg = format!(
-                            "{}'s failure ordering may not be stronger than the success ordering of `{}`",
-                            method,
-                            success_ord,
-                        );
-                        diag.build(&msg)
-                            .help(&format!("consider using {} instead", suggested))
-                            .emit();
-                    });
-                }
-            }
+        let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak])
+            else {return };
+
+        let (success_order_arg, fail_order_arg) = match method {
+            sym::fetch_update => (&args[1], &args[2]),
+            sym::compare_exchange | sym::compare_exchange_weak => (&args[3], &args[4]),
+            _ => return,
+        };
+
+        let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
+
+        if matches!(fail_ordering, sym::Release | sym::AcqRel) {
+            cx.struct_span_lint(INVALID_ATOMIC_ORDERING, fail_order_arg.span, |diag| {
+                diag.build(fluent::lint::atomic_ordering_invalid)
+                    .set_arg("method", method)
+                    .span_label(fail_order_arg.span, fluent::lint::label)
+                    .help(fluent::lint::help)
+                    .emit();
+            });
+        }
+
+        let Some(success_ordering) = Self::match_ordering(cx, success_order_arg) else { return };
+
+        if matches!(
+            (success_ordering, fail_ordering),
+            (sym::Relaxed | sym::Release, sym::Acquire)
+                | (sym::Relaxed | sym::Release | sym::Acquire | sym::AcqRel, sym::SeqCst)
+        ) {
+            let success_suggestion =
+                if success_ordering == sym::Release && fail_ordering == sym::Acquire {
+                    sym::AcqRel
+                } else {
+                    fail_ordering
+                };
+            cx.struct_span_lint(INVALID_ATOMIC_ORDERING, success_order_arg.span, |diag| {
+                diag.build(fluent::lint::atomic_ordering_invalid_fail_success)
+                    .set_arg("method", method)
+                    .set_arg("fail_ordering", fail_ordering)
+                    .set_arg("success_ordering", success_ordering)
+                    .set_arg("success_suggestion", success_suggestion)
+                    .span_label(fail_order_arg.span, fluent::lint::fail_label)
+                    .span_label(success_order_arg.span, fluent::lint::success_label)
+                    .span_suggestion_short(
+                        success_order_arg.span,
+                        fluent::lint::suggestion,
+                        format!("std::sync::atomic::Ordering::{success_suggestion}"),
+                        Applicability::MaybeIncorrect,
+                    )
+                    .emit();
+            });
         }
     }
 }