Skip to content

Commit c9723e5

Browse files
committed
implement host_endian_bytes and the other two
1 parent e3de448 commit c9723e5

File tree

3 files changed

+1014
-1112
lines changed

3 files changed

+1014
-1112
lines changed

clippy_lints/src/endian_bytes.rs

Lines changed: 134 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
use clippy_utils::{
2-
diagnostics::{span_lint_and_help, span_lint_and_then},
3-
is_lint_allowed, match_def_path, path_def_id,
4-
};
1+
use crate::Lint;
2+
use clippy_utils::{diagnostics::span_lint_and_then, is_lint_allowed};
53
use rustc_hir::{Expr, ExprKind};
6-
use rustc_lint::{LateContext, LateLintPass};
4+
use rustc_lint::{LateContext, LateLintPass, LintContext};
5+
use rustc_middle::lint::in_external_macro;
76
use rustc_session::{declare_lint_pass, declare_tool_lint};
7+
use rustc_span::Symbol;
8+
use std::borrow::Cow;
89

910
declare_clippy_lint! {
1011
/// ### What it does
12+
/// Checks for the usage of the `to_ne_bytes` method.
1113
///
1214
/// ### Why is this bad?
15+
/// It's not, but some may prefer to specify the target endianness explicitly.
1316
///
1417
/// ### Example
15-
/// ```rust
16-
/// // example code where clippy issues a warning
17-
/// ```
18-
/// Use instead:
19-
/// ```rust
20-
/// // example code which does not raise clippy warning
18+
/// ```rust,ignore
19+
/// let _x = 2i32.to_ne_bytes();
20+
/// let _y = 2i64.to_ne_bytes();
2121
/// ```
2222
#[clippy::version = "1.71.0"]
2323
pub HOST_ENDIAN_BYTES,
@@ -27,15 +27,16 @@ declare_clippy_lint! {
2727

2828
declare_clippy_lint! {
2929
/// ### What it does
30-
/// Checks for the usage of the `to_ne_bytes` method.
30+
/// Checks for the usage of the `to_le_bytes` method.
3131
///
3232
/// ### Why is this bad?
33-
/// It's not, but some may prefer to specify the target endianness explicitly.
33+
/// It's not, but some may wish to lint usages of this method, either to suggest using the host
34+
/// endianness or big endian.
3435
///
3536
/// ### Example
3637
/// ```rust,ignore
37-
/// let _x = 2i32.to_ne_bytes();
38-
/// let _y = 2i64.to_ne_bytes();
38+
/// let _x = 2i32.to_le_bytes();
39+
/// let _y = 2i64.to_le_bytes();
3940
/// ```
4041
#[clippy::version = "1.71.0"]
4142
pub LITTLE_ENDIAN_BYTES,
@@ -45,13 +46,16 @@ declare_clippy_lint! {
4546

4647
declare_clippy_lint! {
4748
/// ### What it does
48-
/// Checks for the usage of the `to_le_bytes` method.
49+
/// Checks for the usage of the `to_be_bytes` method.
4950
///
5051
/// ### Why is this bad?
52+
/// It's not, but some may wish to lint usages of this method, either to suggest using the host
53+
/// endianness or little endian.
5154
///
5255
/// ### Example
5356
/// ```rust,ignore
54-
/// // example code where clippy issues a warning
57+
/// let _x = 2i32.to_be_bytes();
58+
/// let _y = 2i64.to_be_bytes();
5559
/// ```
5660
#[clippy::version = "1.71.0"]
5761
pub BIG_ENDIAN_BYTES,
@@ -61,78 +65,133 @@ declare_clippy_lint! {
6165

6266
declare_lint_pass!(EndianBytes => [HOST_ENDIAN_BYTES, LITTLE_ENDIAN_BYTES, BIG_ENDIAN_BYTES]);
6367

68+
#[derive(Clone, Debug)]
69+
enum LintKind {
70+
Host,
71+
Little,
72+
Big,
73+
}
74+
75+
impl LintKind {
76+
fn allowed(&self, cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
77+
is_lint_allowed(cx, self.as_lint(), expr.hir_id)
78+
}
79+
80+
fn as_lint(&self) -> &'static Lint {
81+
match self {
82+
LintKind::Host => HOST_ENDIAN_BYTES,
83+
LintKind::Little => LITTLE_ENDIAN_BYTES,
84+
LintKind::Big => BIG_ENDIAN_BYTES,
85+
}
86+
}
87+
88+
fn to_name(&self, prefix: &str) -> String {
89+
match self {
90+
LintKind::Host => format!("{prefix}_ne_bytes"),
91+
LintKind::Little => format!("{prefix}_le_bytes"),
92+
LintKind::Big => format!("{prefix}_be_bytes"),
93+
}
94+
}
95+
}
96+
6497
impl LateLintPass<'_> for EndianBytes {
6598
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
99+
if in_external_macro(cx.sess(), expr.span) {
100+
return;
101+
}
102+
66103
if_chain! {
67104
if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind;
68105
if let ExprKind::Lit(..) = receiver.kind;
69106
if args.is_empty();
107+
if try_lint_endian_bytes(cx, expr, "to", method_name.ident.name);
70108
then {
71-
if method_name.ident.name == sym!(to_ne_bytes) {
72-
span_lint_and_help(
73-
cx,
74-
HOST_ENDIAN_BYTES,
75-
expr.span,
76-
"use of the method `to_ne_bytes`",
77-
None,
78-
"consider specifying the desired endianness",
79-
);
80-
} else if method_name.ident.name == sym!(to_le_bytes) {
81-
span_lint_and_then(cx, LITTLE_ENDIAN_BYTES, expr.span, "use of the method `to_le_bytes`", |diag| {
82-
if is_lint_allowed(cx, BIG_ENDIAN_BYTES, expr.hir_id) {
83-
diag.help("use `to_be_bytes` instead");
84-
}
85-
});
86-
} else if method_name.ident.name == sym!(to_be_bytes) {
87-
span_lint_and_then(cx, BIG_ENDIAN_BYTES, expr.span, "use of the method `to_be_bytes`", |diag| {
88-
if is_lint_allowed(cx, LITTLE_ENDIAN_BYTES, expr.hir_id) {
89-
diag.help("use `to_le_bytes` instead");
90-
}
91-
});
92-
}
93-
94-
// don't waste time also checking from_**_bytes
95109
return;
96110
}
97111
}
98112

99-
span_lint_and_help(
100-
cx,
101-
HOST_ENDIAN_BYTES,
102-
expr.span,
103-
"use of the method `from_ne_bytes`",
104-
None,
105-
&format!("consider specifying the desired endianness: {expr:?}"),
106-
);
107-
108113
if_chain! {
109-
if let ExprKind::Call(function, args) = expr.kind;
110-
if let Some(function_def_id) = path_def_id(cx, function);
111-
if args.len() == 1;
114+
if let ExprKind::Call(function, ..) = expr.kind;
115+
if let ExprKind::Path(qpath) = function.kind;
116+
if let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id();
117+
if let Some(function_name) = cx.get_def_path(def_id).last();
118+
if cx.typeck_results().expr_ty(expr).is_primitive_ty();
112119
then {
113-
if match_def_path(cx, function_def_id, &["from_ne_bytes"]) {
114-
span_lint_and_help(
115-
cx,
116-
HOST_ENDIAN_BYTES,
117-
expr.span,
118-
"use of the method `from_ne_bytes`",
119-
None,
120-
"consider specifying the desired endianness",
121-
);
122-
} else if match_def_path(cx, function_def_id, &["from_le_bytes"]) {
123-
span_lint_and_then(cx, LITTLE_ENDIAN_BYTES, expr.span, "use of the method `from_le_bytes`", |diag| {
124-
if is_lint_allowed(cx, BIG_ENDIAN_BYTES, expr.hir_id) {
125-
diag.help("use `from_be_bytes` instead");
126-
}
127-
});
128-
} else if match_def_path(cx, function_def_id, &["from_be_bytes"]) {
129-
span_lint_and_then(cx, BIG_ENDIAN_BYTES, expr.span, "use of the method `from_be_bytes`", |diag| {
130-
if is_lint_allowed(cx, LITTLE_ENDIAN_BYTES, expr.hir_id) {
131-
diag.help("use `from_le_bytes` instead");
132-
}
133-
});
134-
}
120+
try_lint_endian_bytes(cx, expr, "from", *function_name);
121+
}
122+
}
123+
}
124+
}
125+
126+
fn try_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: &str, name: Symbol) -> bool {
127+
let ne = format!("{prefix}_ne_bytes");
128+
let le = format!("{prefix}_le_bytes");
129+
let be = format!("{prefix}_be_bytes");
130+
131+
let (lint, other_lints) = match name.as_str() {
132+
name if name == ne => ((&LintKind::Host), [(&LintKind::Little), (&LintKind::Big)]),
133+
name if name == le => ((&LintKind::Little), [(&LintKind::Host), (&LintKind::Big)]),
134+
name if name == be => ((&LintKind::Big), [(&LintKind::Host), (&LintKind::Little)]),
135+
_ => return false,
136+
};
137+
138+
let mut help = None;
139+
140+
'build_help: {
141+
// all lints disallowed, don't give help here
142+
if [&[lint], other_lints.as_slice()]
143+
.concat()
144+
.iter()
145+
.all(|lint| !lint.allowed(cx, expr))
146+
{
147+
break 'build_help;
148+
}
149+
150+
// ne_bytes and all other lints allowed
151+
if lint.to_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) {
152+
help = Some(Cow::Borrowed("specify the desired endianness explicitly"));
153+
break 'build_help;
154+
}
155+
156+
// le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but
157+
// le_bytes is not
158+
if (lint.to_name(prefix) == le || lint.to_name(prefix) == be) && LintKind::Host.allowed(cx, expr) {
159+
help = Some(Cow::Borrowed("use the native endianness instead"));
160+
break 'build_help;
161+
}
162+
163+
let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr));
164+
let len = allowed_lints.clone().count();
165+
166+
let mut help_str = "use ".to_owned();
167+
168+
for (i, lint) in allowed_lints.enumerate() {
169+
let only_one = len == 1;
170+
if !only_one {
171+
help_str.push_str("either of ");
172+
}
173+
174+
help_str.push_str(&format!("`{}` ", lint.to_name(prefix)));
175+
176+
if i != len && !only_one {
177+
help_str.push_str("or ");
135178
}
136179
}
180+
181+
help = Some(Cow::Owned(help_str + "instead"));
137182
}
183+
184+
span_lint_and_then(
185+
cx,
186+
lint.as_lint(),
187+
expr.span,
188+
&format!("usage of the method `{}`", lint.to_name(prefix)),
189+
move |diag| {
190+
if let Some(help) = help {
191+
diag.help(help);
192+
}
193+
},
194+
);
195+
196+
true
138197
}

0 commit comments

Comments
 (0)