|
1 | | -use crate::Lint; |
2 | 1 | use clippy_utils::diagnostics::span_lint_and_then; |
| 2 | +use clippy_utils::res::{MaybeDef as _, MaybeTypeckRes as _}; |
3 | 3 | use clippy_utils::{is_lint_allowed, sym}; |
4 | | -use rustc_hir::{Expr, ExprKind}; |
5 | | -use rustc_lint::{LateContext, LateLintPass, LintContext as _}; |
6 | | -use rustc_middle::ty::Ty; |
| 4 | +use core::ptr; |
| 5 | +use rustc_errors::Applicability; |
| 6 | +use rustc_hir::{Expr, ExprKind, QPath}; |
| 7 | +use rustc_lint::{LateContext, LateLintPass}; |
| 8 | +use rustc_middle::ty; |
7 | 9 | use rustc_session::declare_lint_pass; |
8 | | -use rustc_span::Symbol; |
9 | | -use std::fmt::Write as _; |
10 | 10 |
|
11 | 11 | declare_clippy_lint! { |
12 | 12 | /// ### What it does |
@@ -69,133 +69,67 @@ declare_lint_pass!(EndianBytes => [ |
69 | 69 | LITTLE_ENDIAN_BYTES, |
70 | 70 | ]); |
71 | 71 |
|
72 | | -const HOST_NAMES: [Symbol; 2] = [sym::from_ne_bytes, sym::to_ne_bytes]; |
73 | | -const LITTLE_NAMES: [Symbol; 2] = [sym::from_le_bytes, sym::to_le_bytes]; |
74 | | -const BIG_NAMES: [Symbol; 2] = [sym::from_be_bytes, sym::to_be_bytes]; |
75 | | - |
76 | | -#[derive(Clone, Debug)] |
77 | | -enum LintKind { |
78 | | - Host, |
79 | | - Little, |
80 | | - Big, |
81 | | -} |
82 | | - |
83 | | -#[derive(Clone, Copy, PartialEq)] |
84 | | -enum Prefix { |
| 72 | +#[derive(Clone, Copy)] |
| 73 | +enum Direction { |
85 | 74 | From, |
86 | 75 | To, |
87 | 76 | } |
88 | 77 |
|
89 | | -impl LintKind { |
90 | | - fn allowed(&self, cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { |
91 | | - is_lint_allowed(cx, self.as_lint(), expr.hir_id) |
92 | | - } |
93 | | - |
94 | | - fn as_lint(&self) -> &'static Lint { |
95 | | - match self { |
96 | | - LintKind::Host => HOST_ENDIAN_BYTES, |
97 | | - LintKind::Little => LITTLE_ENDIAN_BYTES, |
98 | | - LintKind::Big => BIG_ENDIAN_BYTES, |
99 | | - } |
100 | | - } |
101 | | - |
102 | | - fn as_name(&self, prefix: Prefix) -> Symbol { |
103 | | - let index = usize::from(prefix == Prefix::To); |
104 | | - |
105 | | - match self { |
106 | | - LintKind::Host => HOST_NAMES[index], |
107 | | - LintKind::Little => LITTLE_NAMES[index], |
108 | | - LintKind::Big => BIG_NAMES[index], |
109 | | - } |
110 | | - } |
111 | | -} |
112 | | - |
113 | 78 | impl LateLintPass<'_> for EndianBytes { |
114 | | - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { |
115 | | - let (prefix, name, ty_expr) = match expr.kind { |
116 | | - ExprKind::MethodCall(method_name, receiver, [], ..) => (Prefix::To, method_name.ident.name, receiver), |
117 | | - ExprKind::Call(function, ..) |
118 | | - if let ExprKind::Path(qpath) = function.kind |
119 | | - && let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id() |
120 | | - && let Some(function_name) = cx.get_def_path(def_id).last() => |
121 | | - { |
122 | | - (Prefix::From, *function_name, expr) |
| 79 | + fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { |
| 80 | + let (sp, direction, lint, msg) = match e.kind { |
| 81 | + // rustfmt wants to break each arm into one line per tuple element which |
| 82 | + // really hurts readability. |
| 83 | + #[rustfmt::skip] |
| 84 | + ExprKind::MethodCall(seg, _, [], _) => match seg.ident.name { |
| 85 | + sym::to_ne_bytes => (seg.ident.span, Direction::To, HOST_ENDIAN_BYTES, "use of `to_ne_bytes`"), |
| 86 | + sym::to_le_bytes => (seg.ident.span, Direction::To, LITTLE_ENDIAN_BYTES, "use of `to_le_bytes`"), |
| 87 | + sym::to_be_bytes => (seg.ident.span, Direction::To, BIG_ENDIAN_BYTES, "use of `to_be_bytes`"), |
| 88 | + _ => return, |
| 89 | + }, |
| 90 | + #[rustfmt::skip] |
| 91 | + ExprKind::Path(QPath::TypeRelative(_, seg)) => match seg.ident.name { |
| 92 | + sym::from_ne_bytes => (seg.ident.span, Direction::From, HOST_ENDIAN_BYTES, "use of `from_ne_bytes`"), |
| 93 | + sym::from_le_bytes => (seg.ident.span, Direction::From, LITTLE_ENDIAN_BYTES, "use of `from_le_bytes`"), |
| 94 | + sym::from_be_bytes => (seg.ident.span, Direction::From, BIG_ENDIAN_BYTES, "use of `from_be_bytes`"), |
| 95 | + sym::to_ne_bytes => (seg.ident.span, Direction::To, HOST_ENDIAN_BYTES, "use of `to_ne_bytes`"), |
| 96 | + sym::to_le_bytes => (seg.ident.span, Direction::To, LITTLE_ENDIAN_BYTES, "use of `to_le_bytes`"), |
| 97 | + sym::to_be_bytes => (seg.ident.span, Direction::To, BIG_ENDIAN_BYTES, "use of `to_be_bytes`"), |
| 98 | + _ => return, |
123 | 99 | }, |
124 | 100 | _ => return, |
125 | 101 | }; |
126 | | - if !expr.span.in_external_macro(cx.sess().source_map()) |
127 | | - && let ty = cx.typeck_results().expr_ty(ty_expr) |
128 | | - && ty.is_primitive_ty() |
| 102 | + if let Some(ty) = cx.ty_based_def(e.hir_id).opt_parent(cx).opt_impl_ty(cx) |
| 103 | + && let ty::Uint(_) | ty::Int(_) | ty::Float(_) = *ty.instantiate_identity().skip_normalization().kind() |
| 104 | + // Only check where the name itself comes from. The point of the lints is to |
| 105 | + // catch when the wrong byte order is used so we only care if the current crate |
| 106 | + // decided on the byte order. Which crate actually assembled the path/call |
| 107 | + // isn't relevant for these lints. |
| 108 | + && !sp.in_external_macro(cx.tcx.sess.source_map()) |
129 | 109 | { |
130 | | - maybe_lint_endian_bytes(cx, expr, prefix, name, ty); |
131 | | - } |
132 | | - } |
133 | | -} |
134 | | - |
135 | | -fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) { |
136 | | - let ne = LintKind::Host.as_name(prefix); |
137 | | - let le = LintKind::Little.as_name(prefix); |
138 | | - let be = LintKind::Big.as_name(prefix); |
139 | | - |
140 | | - let (lint, other_lints) = match name { |
141 | | - name if name == ne => ((&LintKind::Host), [(&LintKind::Little), (&LintKind::Big)]), |
142 | | - name if name == le => ((&LintKind::Little), [(&LintKind::Host), (&LintKind::Big)]), |
143 | | - name if name == be => ((&LintKind::Big), [(&LintKind::Host), (&LintKind::Little)]), |
144 | | - _ => return, |
145 | | - }; |
146 | | - |
147 | | - span_lint_and_then( |
148 | | - cx, |
149 | | - lint.as_lint(), |
150 | | - expr.span, |
151 | | - format!( |
152 | | - "usage of the {}`{ty}::{}`{}", |
153 | | - if prefix == Prefix::From { "function " } else { "" }, |
154 | | - lint.as_name(prefix), |
155 | | - if prefix == Prefix::To { " method" } else { "" }, |
156 | | - ), |
157 | | - move |diag| { |
158 | | - // all lints disallowed, don't give help here |
159 | | - if [&[lint], other_lints.as_slice()] |
160 | | - .concat() |
161 | | - .iter() |
162 | | - .all(|lint| !lint.allowed(cx, expr)) |
163 | | - { |
164 | | - return; |
165 | | - } |
166 | | - |
167 | | - // ne_bytes and all other lints allowed |
168 | | - if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) { |
169 | | - diag.help("specify the desired endianness explicitly"); |
170 | | - return; |
171 | | - } |
172 | | - |
173 | | - // le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but |
174 | | - // le_bytes is not |
175 | | - if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) { |
176 | | - diag.help("use the native endianness instead"); |
177 | | - return; |
178 | | - } |
179 | | - |
180 | | - let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr)); |
181 | | - let len = allowed_lints.clone().count(); |
182 | | - |
183 | | - let mut help_str = "use ".to_owned(); |
184 | | - |
185 | | - for (i, lint) in allowed_lints.enumerate() { |
186 | | - let only_one = len == 1; |
187 | | - if !only_one { |
188 | | - help_str.push_str("either of "); |
| 110 | + span_lint_and_then(cx, lint, sp, msg, |diag| { |
| 111 | + if !ptr::addr_eq(lint, HOST_ENDIAN_BYTES) && is_lint_allowed(cx, HOST_ENDIAN_BYTES, e.hir_id) { |
| 112 | + let (msg, sugg) = match direction { |
| 113 | + Direction::From => ("convert from native endian", "from_ne_bytes"), |
| 114 | + Direction::To => ("convert to native endian", "to_ne_bytes"), |
| 115 | + }; |
| 116 | + diag.span_suggestion(sp, msg, sugg, Applicability::MaybeIncorrect); |
189 | 117 | } |
190 | | - |
191 | | - write!(help_str, "`{ty}::{}` ", lint.as_name(prefix)).unwrap(); |
192 | | - |
193 | | - if i != len && !only_one { |
194 | | - help_str.push_str("or "); |
| 118 | + if !ptr::addr_eq(lint, LITTLE_ENDIAN_BYTES) && is_lint_allowed(cx, LITTLE_ENDIAN_BYTES, e.hir_id) { |
| 119 | + let (msg, sugg) = match direction { |
| 120 | + Direction::From => ("convert from little endian", "from_le_bytes"), |
| 121 | + Direction::To => ("convert to little endian", "to_le_bytes"), |
| 122 | + }; |
| 123 | + diag.span_suggestion(sp, msg, sugg, Applicability::MaybeIncorrect); |
195 | 124 | } |
196 | | - } |
197 | | - help_str.push_str("instead"); |
198 | | - diag.help(help_str); |
199 | | - }, |
200 | | - ); |
| 125 | + if !ptr::addr_eq(lint, BIG_ENDIAN_BYTES) && is_lint_allowed(cx, BIG_ENDIAN_BYTES, e.hir_id) { |
| 126 | + let (msg, sugg) = match direction { |
| 127 | + Direction::From => ("convert from big endian", "from_be_bytes"), |
| 128 | + Direction::To => ("convert to big endian", "to_be_bytes"), |
| 129 | + }; |
| 130 | + diag.span_suggestion(sp, msg, sugg, Applicability::MaybeIncorrect); |
| 131 | + } |
| 132 | + }); |
| 133 | + } |
| 134 | + } |
201 | 135 | } |
0 commit comments