The `restriction` group contains many lints which are not about necessarily “bad” things, but style choices — perhaps even style choices which contradict conventional Rust style — or are otherwise very situational. This results in silly wording like “Why is this bad? It isn't, but ...”, which I’ve seen confuse a newcomer at least once. To improve this situation, this commit replaces the “Why is this bad?” section heading with “Why restrict this?”, for most, but not all, restriction lints. I left alone the ones whose placement in the restriction group is more incidental. In order to make this make sense, I had to remove the “It isn't, but” texts from the contents of the sections. Sometimes further changes were needed, or there were obvious fixes to make, and I went ahead and made those changes without attempting to split them into another commit, even though many of them are not strictly necessary for the “Why restrict this?” project.
214 lines
6.6 KiB
Rust
214 lines
6.6 KiB
Rust
use crate::Lint;
|
||
use clippy_utils::diagnostics::span_lint_and_then;
|
||
use clippy_utils::is_lint_allowed;
|
||
use rustc_hir::{Expr, ExprKind};
|
||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||
use rustc_middle::lint::in_external_macro;
|
||
use rustc_middle::ty::Ty;
|
||
use rustc_session::declare_lint_pass;
|
||
use rustc_span::Symbol;
|
||
use std::borrow::Cow;
|
||
|
||
declare_clippy_lint! {
|
||
/// ### What it does
|
||
/// Checks for the usage of the `to_ne_bytes` method and/or the function `from_ne_bytes`.
|
||
///
|
||
/// ### Why restrict this?
|
||
/// To ensure use of explicitly chosen endianness rather than the target’s endianness,
|
||
/// such as when implementing network protocols or file formats rather than FFI.
|
||
///
|
||
/// ### Example
|
||
/// ```rust,ignore
|
||
/// let _x = 2i32.to_ne_bytes();
|
||
/// let _y = 2i64.to_ne_bytes();
|
||
/// ```
|
||
#[clippy::version = "1.72.0"]
|
||
pub HOST_ENDIAN_BYTES,
|
||
restriction,
|
||
"disallows usage of the `to_ne_bytes` method"
|
||
}
|
||
|
||
declare_clippy_lint! {
|
||
/// ### What it does
|
||
/// Checks for the usage of the `to_le_bytes` method and/or the function `from_le_bytes`.
|
||
///
|
||
/// ### Why restrict this?
|
||
/// To ensure use of big endian or the target’s endianness rather than little endian.
|
||
///
|
||
/// ### Example
|
||
/// ```rust,ignore
|
||
/// let _x = 2i32.to_le_bytes();
|
||
/// let _y = 2i64.to_le_bytes();
|
||
/// ```
|
||
#[clippy::version = "1.72.0"]
|
||
pub LITTLE_ENDIAN_BYTES,
|
||
restriction,
|
||
"disallows usage of the `to_le_bytes` method"
|
||
}
|
||
|
||
declare_clippy_lint! {
|
||
/// ### What it does
|
||
/// Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`.
|
||
///
|
||
/// ### Why restrict this?
|
||
/// To ensure use of little endian or the target’s endianness rather than big endian.
|
||
///
|
||
/// ### Example
|
||
/// ```rust,ignore
|
||
/// let _x = 2i32.to_be_bytes();
|
||
/// let _y = 2i64.to_be_bytes();
|
||
/// ```
|
||
#[clippy::version = "1.72.0"]
|
||
pub BIG_ENDIAN_BYTES,
|
||
restriction,
|
||
"disallows usage of the `to_be_bytes` method"
|
||
}
|
||
|
||
declare_lint_pass!(EndianBytes => [HOST_ENDIAN_BYTES, LITTLE_ENDIAN_BYTES, BIG_ENDIAN_BYTES]);
|
||
|
||
const HOST_NAMES: [&str; 2] = ["from_ne_bytes", "to_ne_bytes"];
|
||
const LITTLE_NAMES: [&str; 2] = ["from_le_bytes", "to_le_bytes"];
|
||
const BIG_NAMES: [&str; 2] = ["from_be_bytes", "to_be_bytes"];
|
||
|
||
#[derive(Clone, Debug)]
|
||
enum LintKind {
|
||
Host,
|
||
Little,
|
||
Big,
|
||
}
|
||
|
||
#[derive(Clone, Copy, PartialEq)]
|
||
enum Prefix {
|
||
From,
|
||
To,
|
||
}
|
||
|
||
impl LintKind {
|
||
fn allowed(&self, cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||
is_lint_allowed(cx, self.as_lint(), expr.hir_id)
|
||
}
|
||
|
||
fn as_lint(&self) -> &'static Lint {
|
||
match self {
|
||
LintKind::Host => HOST_ENDIAN_BYTES,
|
||
LintKind::Little => LITTLE_ENDIAN_BYTES,
|
||
LintKind::Big => BIG_ENDIAN_BYTES,
|
||
}
|
||
}
|
||
|
||
fn as_name(&self, prefix: Prefix) -> &str {
|
||
let index = usize::from(prefix == Prefix::To);
|
||
|
||
match self {
|
||
LintKind::Host => HOST_NAMES[index],
|
||
LintKind::Little => LITTLE_NAMES[index],
|
||
LintKind::Big => BIG_NAMES[index],
|
||
}
|
||
}
|
||
}
|
||
|
||
impl LateLintPass<'_> for EndianBytes {
|
||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||
if in_external_macro(cx.sess(), expr.span) {
|
||
return;
|
||
}
|
||
|
||
if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind
|
||
&& args.is_empty()
|
||
&& let ty = cx.typeck_results().expr_ty(receiver)
|
||
&& ty.is_primitive_ty()
|
||
&& maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if let ExprKind::Call(function, ..) = expr.kind
|
||
&& let ExprKind::Path(qpath) = function.kind
|
||
&& let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id()
|
||
&& let Some(function_name) = cx.get_def_path(def_id).last()
|
||
&& let ty = cx.typeck_results().expr_ty(expr)
|
||
&& ty.is_primitive_ty()
|
||
{
|
||
maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty);
|
||
}
|
||
}
|
||
}
|
||
|
||
fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) -> bool {
|
||
let ne = LintKind::Host.as_name(prefix);
|
||
let le = LintKind::Little.as_name(prefix);
|
||
let be = LintKind::Big.as_name(prefix);
|
||
|
||
let (lint, other_lints) = match name.as_str() {
|
||
name if name == ne => ((&LintKind::Host), [(&LintKind::Little), (&LintKind::Big)]),
|
||
name if name == le => ((&LintKind::Little), [(&LintKind::Host), (&LintKind::Big)]),
|
||
name if name == be => ((&LintKind::Big), [(&LintKind::Host), (&LintKind::Little)]),
|
||
_ => return false,
|
||
};
|
||
|
||
let mut help = None;
|
||
|
||
'build_help: {
|
||
// all lints disallowed, don't give help here
|
||
if [&[lint], other_lints.as_slice()]
|
||
.concat()
|
||
.iter()
|
||
.all(|lint| !lint.allowed(cx, expr))
|
||
{
|
||
break 'build_help;
|
||
}
|
||
|
||
// ne_bytes and all other lints allowed
|
||
if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) {
|
||
help = Some(Cow::Borrowed("specify the desired endianness explicitly"));
|
||
break 'build_help;
|
||
}
|
||
|
||
// le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but
|
||
// le_bytes is not
|
||
if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) {
|
||
help = Some(Cow::Borrowed("use the native endianness instead"));
|
||
break 'build_help;
|
||
}
|
||
|
||
let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr));
|
||
let len = allowed_lints.clone().count();
|
||
|
||
let mut help_str = "use ".to_owned();
|
||
|
||
for (i, lint) in allowed_lints.enumerate() {
|
||
let only_one = len == 1;
|
||
if !only_one {
|
||
help_str.push_str("either of ");
|
||
}
|
||
|
||
help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix)));
|
||
|
||
if i != len && !only_one {
|
||
help_str.push_str("or ");
|
||
}
|
||
}
|
||
|
||
help = Some(Cow::Owned(help_str + "instead"));
|
||
}
|
||
|
||
span_lint_and_then(
|
||
cx,
|
||
lint.as_lint(),
|
||
expr.span,
|
||
format!(
|
||
"usage of the {}`{ty}::{}`{}",
|
||
if prefix == Prefix::From { "function " } else { "" },
|
||
lint.as_name(prefix),
|
||
if prefix == Prefix::To { " method" } else { "" },
|
||
),
|
||
move |diag| {
|
||
if let Some(help) = help {
|
||
diag.help(help);
|
||
}
|
||
},
|
||
);
|
||
|
||
true
|
||
}
|