Skip to content

Commit eacd095

Browse files
committed
Auto merge of #10826 - Centri3:endian_bytes, r=Manishearth
Add lints for disallowing usage of `to_xx_bytes` and `from_xx_bytes` Adds `host_endian_bytes`, `little_endian_bytes` and `big_endian_bytes` Closes #10765 v - not sure what to put here since this adds 3 lints changelog: Add `host_endian_bytes`, `little_endian_bytes` and `big_endian_bytes` lints
2 parents 1841661 + 7fe200e commit eacd095

File tree

6 files changed

+1382
-0
lines changed

6 files changed

+1382
-0
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -4641,6 +4641,7 @@ Released 2018-09-13
46414641
[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
46424642
[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
46434643
[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
4644+
[`big_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#big_endian_bytes
46444645
[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
46454646
[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
46464647
[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
@@ -4811,6 +4812,7 @@ Released 2018-09-13
48114812
[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first
48124813
[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
48134814
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
4815+
[`host_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#host_endian_bytes
48144816
[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
48154817
[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
48164818
[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
@@ -4892,6 +4894,7 @@ Released 2018-09-13
48924894
[`let_with_type_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_with_type_underscore
48934895
[`lines_filter_map_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok
48944896
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
4897+
[`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes
48954898
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
48964899
[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
48974900
[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports

clippy_lints/src/declared_lints.rs

+3
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
145145
crate::empty_drop::EMPTY_DROP_INFO,
146146
crate::empty_enum::EMPTY_ENUM_INFO,
147147
crate::empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO,
148+
crate::endian_bytes::BIG_ENDIAN_BYTES_INFO,
149+
crate::endian_bytes::HOST_ENDIAN_BYTES_INFO,
150+
crate::endian_bytes::LITTLE_ENDIAN_BYTES_INFO,
148151
crate::entry::MAP_ENTRY_INFO,
149152
crate::enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT_INFO,
150153
crate::enum_variants::ENUM_VARIANT_NAMES_INFO,

clippy_lints/src/endian_bytes.rs

+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
use crate::Lint;
2+
use clippy_utils::{diagnostics::span_lint_and_then, is_lint_allowed};
3+
use rustc_hir::{Expr, ExprKind};
4+
use rustc_lint::{LateContext, LateLintPass, LintContext};
5+
use rustc_middle::{lint::in_external_macro, ty::Ty};
6+
use rustc_session::{declare_lint_pass, declare_tool_lint};
7+
use rustc_span::Symbol;
8+
use std::borrow::Cow;
9+
10+
declare_clippy_lint! {
11+
/// ### What it does
12+
/// Checks for the usage of the `to_ne_bytes` method and/or the function `from_ne_bytes`.
13+
///
14+
/// ### Why is this bad?
15+
/// It's not, but some may prefer to specify the target endianness explicitly.
16+
///
17+
/// ### Example
18+
/// ```rust,ignore
19+
/// let _x = 2i32.to_ne_bytes();
20+
/// let _y = 2i64.to_ne_bytes();
21+
/// ```
22+
#[clippy::version = "1.71.0"]
23+
pub HOST_ENDIAN_BYTES,
24+
restriction,
25+
"disallows usage of the `to_ne_bytes` method"
26+
}
27+
28+
declare_clippy_lint! {
29+
/// ### What it does
30+
/// Checks for the usage of the `to_le_bytes` method and/or the function `from_le_bytes`.
31+
///
32+
/// ### Why is this bad?
33+
/// It's not, but some may wish to lint usage of this method, either to suggest using the host
34+
/// endianness or big endian.
35+
///
36+
/// ### Example
37+
/// ```rust,ignore
38+
/// let _x = 2i32.to_le_bytes();
39+
/// let _y = 2i64.to_le_bytes();
40+
/// ```
41+
#[clippy::version = "1.71.0"]
42+
pub LITTLE_ENDIAN_BYTES,
43+
restriction,
44+
"disallows usage of the `to_le_bytes` method"
45+
}
46+
47+
declare_clippy_lint! {
48+
/// ### What it does
49+
/// Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`.
50+
///
51+
/// ### Why is this bad?
52+
/// It's not, but some may wish to lint usage of this method, either to suggest using the host
53+
/// endianness or little endian.
54+
///
55+
/// ### Example
56+
/// ```rust,ignore
57+
/// let _x = 2i32.to_be_bytes();
58+
/// let _y = 2i64.to_be_bytes();
59+
/// ```
60+
#[clippy::version = "1.71.0"]
61+
pub BIG_ENDIAN_BYTES,
62+
restriction,
63+
"disallows usage of the `to_be_bytes` method"
64+
}
65+
66+
declare_lint_pass!(EndianBytes => [HOST_ENDIAN_BYTES, LITTLE_ENDIAN_BYTES, BIG_ENDIAN_BYTES]);
67+
68+
const HOST_NAMES: [&str; 2] = ["from_ne_bytes", "to_ne_bytes"];
69+
const LITTLE_NAMES: [&str; 2] = ["from_le_bytes", "to_le_bytes"];
70+
const BIG_NAMES: [&str; 2] = ["from_be_bytes", "to_be_bytes"];
71+
72+
#[derive(Clone, Debug)]
73+
enum LintKind {
74+
Host,
75+
Little,
76+
Big,
77+
}
78+
79+
#[derive(Clone, Copy, PartialEq)]
80+
enum Prefix {
81+
From,
82+
To,
83+
}
84+
85+
impl LintKind {
86+
fn allowed(&self, cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
87+
is_lint_allowed(cx, self.as_lint(), expr.hir_id)
88+
}
89+
90+
fn as_lint(&self) -> &'static Lint {
91+
match self {
92+
LintKind::Host => HOST_ENDIAN_BYTES,
93+
LintKind::Little => LITTLE_ENDIAN_BYTES,
94+
LintKind::Big => BIG_ENDIAN_BYTES,
95+
}
96+
}
97+
98+
fn as_name(&self, prefix: Prefix) -> &str {
99+
let index = usize::from(prefix == Prefix::To);
100+
101+
match self {
102+
LintKind::Host => HOST_NAMES[index],
103+
LintKind::Little => LITTLE_NAMES[index],
104+
LintKind::Big => BIG_NAMES[index],
105+
}
106+
}
107+
}
108+
109+
impl LateLintPass<'_> for EndianBytes {
110+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
111+
if in_external_macro(cx.sess(), expr.span) {
112+
return;
113+
}
114+
115+
if_chain! {
116+
if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind;
117+
if args.is_empty();
118+
let ty = cx.typeck_results().expr_ty(receiver);
119+
if ty.is_primitive_ty();
120+
if maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty);
121+
then {
122+
return;
123+
}
124+
}
125+
126+
if_chain! {
127+
if let ExprKind::Call(function, ..) = expr.kind;
128+
if let ExprKind::Path(qpath) = function.kind;
129+
if let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id();
130+
if let Some(function_name) = cx.get_def_path(def_id).last();
131+
let ty = cx.typeck_results().expr_ty(expr);
132+
if ty.is_primitive_ty();
133+
then {
134+
maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty);
135+
}
136+
}
137+
}
138+
}
139+
140+
fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) -> bool {
141+
let ne = LintKind::Host.as_name(prefix);
142+
let le = LintKind::Little.as_name(prefix);
143+
let be = LintKind::Big.as_name(prefix);
144+
145+
let (lint, other_lints) = match name.as_str() {
146+
name if name == ne => ((&LintKind::Host), [(&LintKind::Little), (&LintKind::Big)]),
147+
name if name == le => ((&LintKind::Little), [(&LintKind::Host), (&LintKind::Big)]),
148+
name if name == be => ((&LintKind::Big), [(&LintKind::Host), (&LintKind::Little)]),
149+
_ => return false,
150+
};
151+
152+
let mut help = None;
153+
154+
'build_help: {
155+
// all lints disallowed, don't give help here
156+
if [&[lint], other_lints.as_slice()]
157+
.concat()
158+
.iter()
159+
.all(|lint| !lint.allowed(cx, expr))
160+
{
161+
break 'build_help;
162+
}
163+
164+
// ne_bytes and all other lints allowed
165+
if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) {
166+
help = Some(Cow::Borrowed("specify the desired endianness explicitly"));
167+
break 'build_help;
168+
}
169+
170+
// le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but
171+
// le_bytes is not
172+
if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) {
173+
help = Some(Cow::Borrowed("use the native endianness instead"));
174+
break 'build_help;
175+
}
176+
177+
let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr));
178+
let len = allowed_lints.clone().count();
179+
180+
let mut help_str = "use ".to_owned();
181+
182+
for (i, lint) in allowed_lints.enumerate() {
183+
let only_one = len == 1;
184+
if !only_one {
185+
help_str.push_str("either of ");
186+
}
187+
188+
help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix)));
189+
190+
if i != len && !only_one {
191+
help_str.push_str("or ");
192+
}
193+
}
194+
195+
help = Some(Cow::Owned(help_str + "instead"));
196+
}
197+
198+
span_lint_and_then(
199+
cx,
200+
lint.as_lint(),
201+
expr.span,
202+
&format!(
203+
"usage of the {}`{ty}::{}`{}",
204+
if prefix == Prefix::From { "function " } else { "" },
205+
lint.as_name(prefix),
206+
if prefix == Prefix::To { " method" } else { "" },
207+
),
208+
move |diag| {
209+
if let Some(help) = help {
210+
diag.help(help);
211+
}
212+
},
213+
);
214+
215+
true
216+
}

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ mod else_if_without_else;
114114
mod empty_drop;
115115
mod empty_enum;
116116
mod empty_structs_with_brackets;
117+
mod endian_bytes;
117118
mod entry;
118119
mod enum_clike;
119120
mod enum_variants;
@@ -1010,6 +1011,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10101011
store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs));
10111012
store.register_early_pass(|| Box::new(needless_else::NeedlessElse));
10121013
store.register_late_pass(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug));
1014+
store.register_late_pass(|_| Box::new(endian_bytes::EndianBytes));
10131015
// add lints here, do not remove this comment, it's used in `new_lint`
10141016
}
10151017

tests/ui/endian_bytes.rs

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#![allow(unused)]
2+
#![allow(clippy::diverging_sub_expression)]
3+
#![no_main]
4+
5+
macro_rules! fn_body {
6+
() => {
7+
2u8.to_ne_bytes();
8+
2i8.to_ne_bytes();
9+
2u16.to_ne_bytes();
10+
2i16.to_ne_bytes();
11+
2u32.to_ne_bytes();
12+
2i32.to_ne_bytes();
13+
2u64.to_ne_bytes();
14+
2i64.to_ne_bytes();
15+
2u128.to_ne_bytes();
16+
2i128.to_ne_bytes();
17+
2.0f32.to_ne_bytes();
18+
2.0f64.to_ne_bytes();
19+
2usize.to_ne_bytes();
20+
2isize.to_ne_bytes();
21+
u8::from_ne_bytes(todo!());
22+
i8::from_ne_bytes(todo!());
23+
u16::from_ne_bytes(todo!());
24+
i16::from_ne_bytes(todo!());
25+
u32::from_ne_bytes(todo!());
26+
i32::from_ne_bytes(todo!());
27+
u64::from_ne_bytes(todo!());
28+
i64::from_ne_bytes(todo!());
29+
u128::from_ne_bytes(todo!());
30+
i128::from_ne_bytes(todo!());
31+
usize::from_ne_bytes(todo!());
32+
isize::from_ne_bytes(todo!());
33+
f32::from_ne_bytes(todo!());
34+
f64::from_ne_bytes(todo!());
35+
36+
2u8.to_le_bytes();
37+
2i8.to_le_bytes();
38+
2u16.to_le_bytes();
39+
2i16.to_le_bytes();
40+
2u32.to_le_bytes();
41+
2i32.to_le_bytes();
42+
2u64.to_le_bytes();
43+
2i64.to_le_bytes();
44+
2u128.to_le_bytes();
45+
2i128.to_le_bytes();
46+
2.0f32.to_le_bytes();
47+
2.0f64.to_le_bytes();
48+
2usize.to_le_bytes();
49+
2isize.to_le_bytes();
50+
u8::from_le_bytes(todo!());
51+
i8::from_le_bytes(todo!());
52+
u16::from_le_bytes(todo!());
53+
i16::from_le_bytes(todo!());
54+
u32::from_le_bytes(todo!());
55+
i32::from_le_bytes(todo!());
56+
u64::from_le_bytes(todo!());
57+
i64::from_le_bytes(todo!());
58+
u128::from_le_bytes(todo!());
59+
i128::from_le_bytes(todo!());
60+
usize::from_le_bytes(todo!());
61+
isize::from_le_bytes(todo!());
62+
f32::from_le_bytes(todo!());
63+
f64::from_le_bytes(todo!());
64+
};
65+
}
66+
67+
// bless breaks if I use fn_body too much (oops)
68+
macro_rules! fn_body_smol {
69+
() => {
70+
2u8.to_ne_bytes();
71+
u8::from_ne_bytes(todo!());
72+
73+
2u8.to_le_bytes();
74+
u8::from_le_bytes(todo!());
75+
76+
2u8.to_be_bytes();
77+
u8::from_be_bytes(todo!());
78+
};
79+
}
80+
81+
#[rustfmt::skip]
82+
#[warn(clippy::host_endian_bytes)]
83+
fn host() { fn_body!(); }
84+
85+
#[rustfmt::skip]
86+
#[warn(clippy::little_endian_bytes)]
87+
fn little() { fn_body!(); }
88+
89+
#[rustfmt::skip]
90+
#[warn(clippy::big_endian_bytes)]
91+
fn big() { fn_body!(); }
92+
93+
#[rustfmt::skip]
94+
#[warn(clippy::host_endian_bytes)]
95+
#[warn(clippy::big_endian_bytes)]
96+
fn host_encourage_little() { fn_body_smol!(); }
97+
98+
#[rustfmt::skip]
99+
#[warn(clippy::host_endian_bytes)]
100+
#[warn(clippy::little_endian_bytes)]
101+
fn host_encourage_big() { fn_body_smol!(); }
102+
103+
#[rustfmt::skip]
104+
#[warn(clippy::host_endian_bytes)]
105+
#[warn(clippy::little_endian_bytes)]
106+
#[warn(clippy::big_endian_bytes)]
107+
fn no_help() { fn_body_smol!(); }
108+
109+
#[rustfmt::skip]
110+
#[warn(clippy::little_endian_bytes)]
111+
#[warn(clippy::big_endian_bytes)]
112+
fn little_encourage_host() { fn_body_smol!(); }
113+
114+
#[rustfmt::skip]
115+
#[warn(clippy::host_endian_bytes)]
116+
#[warn(clippy::little_endian_bytes)]
117+
fn little_encourage_big() { fn_body_smol!(); }
118+
119+
#[rustfmt::skip]
120+
#[warn(clippy::big_endian_bytes)]
121+
#[warn(clippy::little_endian_bytes)]
122+
fn big_encourage_host() { fn_body_smol!(); }
123+
124+
#[rustfmt::skip]
125+
#[warn(clippy::host_endian_bytes)]
126+
#[warn(clippy::big_endian_bytes)]
127+
fn big_encourage_little() { fn_body_smol!(); }

0 commit comments

Comments
 (0)