Skip to content

Commit 4826743

Browse files
committed
Add size_of_ref lint
Fixes #9995
1 parent b3145fe commit 4826743

File tree

6 files changed

+133
-0
lines changed

6 files changed

+133
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4546,6 +4546,7 @@ Released 2018-09-13
45464546
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
45474547
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
45484548
[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
4549+
[`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref
45494550
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
45504551
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
45514552
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
536536
crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO,
537537
crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO,
538538
crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
539+
crate::size_of_ref::SIZE_OF_REF_INFO,
539540
crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO,
540541
crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO,
541542
crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ mod shadow;
264264
mod single_char_lifetime_names;
265265
mod single_component_path_imports;
266266
mod size_of_in_element_count;
267+
mod size_of_ref;
267268
mod slow_vector_initialization;
268269
mod std_instead_of_core;
269270
mod strings;
@@ -904,6 +905,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
904905
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv())));
905906
store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock));
906907
store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck));
908+
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
907909
// add lints here, do not remove this comment, it's used in `new_lint`
908910
}
909911

clippy_lints/src/size_of_ref.rs

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use clippy_utils::diagnostics::span_lint_and_help;
2+
use clippy_utils::ty::peel_mid_ty_refs;
3+
use rustc_hir::{Expr, ExprKind};
4+
use rustc_lint::{LateContext, LateLintPass};
5+
use rustc_session::{declare_lint_pass, declare_tool_lint};
6+
use rustc_span::sym;
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
///
11+
/// Checks for calls to `std::mem::size_of_val()` where the argument is
12+
/// a reference to a reference.
13+
///
14+
/// ### Why is this bad?
15+
///
16+
/// The result of calling `size_of_val()` with a reference to a reference
17+
/// as the argument will be the size of any generic reference-type, not
18+
/// the size of the value behind the reference.
19+
///
20+
/// ### Example
21+
/// ```rust
22+
/// struct Foo {
23+
/// buffer: [u8],
24+
/// }
25+
///
26+
/// impl Foo {
27+
/// fn size(&self) -> usize {
28+
/// // Note that `&self` as an argument is a `&&Foo`: Bacause `self`
29+
/// // is already a reference, `&self` is a double-reference,
30+
/// // and the return value of `size_of_val()` therefor is the
31+
/// // size of any generic reference-type.
32+
/// std::mem::size_of_val(&self)
33+
/// }
34+
/// }
35+
/// ```
36+
/// Use instead:
37+
/// ```rust
38+
/// struct Foo {
39+
/// buffer: [u8],
40+
/// }
41+
///
42+
/// impl Foo {
43+
/// fn size(&self) -> usize {
44+
/// // Correct
45+
/// std::mem::size_of_val(self)
46+
/// }
47+
/// }
48+
/// ```
49+
#[clippy::version = "1.67.0"]
50+
pub SIZE_OF_REF,
51+
correctness,
52+
"Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended"
53+
}
54+
declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]);
55+
56+
impl LateLintPass<'_> for SizeOfRef {
57+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
58+
if let ExprKind::Call(path, [arg]) = expr.kind
59+
&& let ExprKind::Path(ref qpath) = path.kind
60+
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
61+
&& cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id)
62+
&& let arg_ty = cx.typeck_results().expr_ty(arg)
63+
&& peel_mid_ty_refs(arg_ty).1 > 1
64+
{
65+
span_lint_and_help(
66+
cx,
67+
SIZE_OF_REF,
68+
expr.span,
69+
"argument to `std::mem::size_of_val()` is a reference to a reference",
70+
None,
71+
"dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the generic size of any reference-type",
72+
);
73+
}
74+
}
75+
}

tests/ui/size_of_ref.rs

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#![allow(unused)]
2+
#![warn(clippy::size_of_ref)]
3+
4+
use std::mem::size_of_val;
5+
6+
fn main() {
7+
let x = 5;
8+
let y = &x;
9+
10+
size_of_val(&x); // no lint
11+
size_of_val(y); // no lint
12+
13+
size_of_val(&&x);
14+
size_of_val(&y);
15+
}
16+
17+
struct S {
18+
field: u32,
19+
data: Vec<u8>,
20+
}
21+
22+
impl S {
23+
/// Get size of object including `self`, in bytes.
24+
pub fn size(&self) -> usize {
25+
std::mem::size_of_val(&self) + (std::mem::size_of::<u8>() * self.data.capacity())
26+
}
27+
}

tests/ui/size_of_ref.stderr

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error: argument to `std::mem::size_of_val()` is a reference to a reference
2+
--> $DIR/size_of_ref.rs:13:5
3+
|
4+
LL | size_of_val(&&x);
5+
| ^^^^^^^^^^^^^^^^
6+
|
7+
= help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the generic size of any reference-type
8+
= note: `-D clippy::size-of-ref` implied by `-D warnings`
9+
10+
error: argument to `std::mem::size_of_val()` is a reference to a reference
11+
--> $DIR/size_of_ref.rs:14:5
12+
|
13+
LL | size_of_val(&y);
14+
| ^^^^^^^^^^^^^^^
15+
|
16+
= help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the generic size of any reference-type
17+
18+
error: argument to `std::mem::size_of_val()` is a reference to a reference
19+
--> $DIR/size_of_ref.rs:25:9
20+
|
21+
LL | std::mem::size_of_val(&self) + (std::mem::size_of::<u8>() * self.data.capacity())
22+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
23+
|
24+
= help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the generic size of any reference-type
25+
26+
error: aborting due to 3 previous errors
27+

0 commit comments

Comments
 (0)