]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/manual_bits.rs
Update lint description of `clippy::manual_bits` to include the `as usize` cast
[rust.git] / clippy_lints / src / manual_bits.rs
index 809aa168a7a0ee2f8152dc3f0b7884a9f20dba5e..ac3d9447b6bd3d35fc2472e35ca7dd00ad84eb9e 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
-use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{get_parent_expr, meets_msrv, msrvs};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
@@ -8,6 +8,7 @@
 use rustc_middle::ty::{self, Ty};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -23,7 +24,7 @@
     /// ```
     /// Use instead:
     /// ```rust
-    /// usize::BITS;
+    /// usize::BITS as usize;
     /// ```
     #[clippy::version = "1.60.0"]
     pub MANUAL_BITS,
@@ -58,16 +59,19 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
             if let ExprKind::Lit(lit) = &other_expr.kind;
             if let LitKind::Int(8, _) = lit.node;
-
             then {
+                let mut app = Applicability::MachineApplicable;
+                let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app);
+                let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
+
                 span_lint_and_sugg(
                     cx,
                     MANUAL_BITS,
                     expr.span,
                     "usage of `mem::size_of::<T>()` to obtain the size of `T` in bits",
                     "consider using",
-                    format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()),
-                    Applicability::MachineApplicable,
+                    sugg,
+                    app,
                 );
             }
         }
@@ -99,7 +103,7 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<
         if let Some(GenericArg::Type(real_ty)) = args.args.get(0);
 
         if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
-        if match_def_path(cx, def_id, &paths::MEM_SIZE_OF);
+        if cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id);
         then {
             cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (real_ty, resolved_ty))
         } else {
@@ -107,3 +111,36 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<
         }
     }
 }
+
+fn create_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, base_sugg: String) -> String {
+    if let Some(parent_expr) = get_parent_expr(cx, expr) {
+        if is_ty_conversion(parent_expr) {
+            return base_sugg;
+        }
+
+        // These expressions have precedence over casts, the suggestion therefore
+        // needs to be wrapped into parentheses
+        match parent_expr.kind {
+            ExprKind::Unary(..) | ExprKind::AddrOf(..) | ExprKind::MethodCall(..) => {
+                return format!("({base_sugg} as usize)");
+            },
+            _ => {},
+        }
+    }
+
+    format!("{base_sugg} as usize")
+}
+
+fn is_ty_conversion(expr: &Expr<'_>) -> bool {
+    if let ExprKind::Cast(..) = expr.kind {
+        true
+    } else if let ExprKind::MethodCall(path, [_], _) = expr.kind
+        && path.ident.name == rustc_span::sym::try_into
+    {
+        // This is only called for `usize` which implements `TryInto`. Therefore,
+        // we don't have to check here if `self` implements the `TryInto` trait.
+        true
+    } else {
+        false
+    }
+}