]>
Commit | Line | Data |
---|---|---|
9ffffee4 FG |
1 | use crate::{ |
2 | fluent_generated as fluent, | |
3 | lints::{ | |
4 | AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes, | |
5 | InvalidAtomicOrderingDiag, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, | |
6 | OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, | |
7 | OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, VariantSizeDifferencesDiag, | |
8 | }, | |
9c376795 | 9 | }; |
dfeec247 | 10 | use crate::{LateContext, LateLintPass, LintContext}; |
3dfed10e | 11 | use rustc_ast as ast; |
74b04a01 | 12 | use rustc_attr as attr; |
dfeec247 | 13 | use rustc_data_structures::fx::FxHashSet; |
9ffffee4 | 14 | use rustc_errors::DiagnosticMessage; |
dfeec247 | 15 | use rustc_hir as hir; |
94222f64 | 16 | use rustc_hir::{is_range_literal, Expr, ExprKind, Node}; |
c295e0f8 | 17 | use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton}; |
ba9703b0 | 18 | use rustc_middle::ty::subst::SubstsRef; |
9ffffee4 FG |
19 | use rustc_middle::ty::{ |
20 | self, AdtKind, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, | |
21 | }; | |
22 | use rustc_span::def_id::LocalDefId; | |
dfeec247 XL |
23 | use rustc_span::source_map; |
24 | use rustc_span::symbol::sym; | |
2b03887a | 25 | use rustc_span::{Span, Symbol}; |
487cf647 | 26 | use rustc_target::abi::{Abi, Size, WrappingRange}; |
c295e0f8 | 27 | use rustc_target::abi::{Integer, TagEncoding, Variants}; |
3dfed10e | 28 | use rustc_target::spec::abi::Abi as SpecAbi; |
9fa01778 | 29 | |
cdc7bbd5 | 30 | use std::iter; |
29967ef6 | 31 | use std::ops::ControlFlow; |
9fa01778 | 32 | |
b039eaaf | 33 | declare_lint! { |
1b1a35ee XL |
34 | /// The `unused_comparisons` lint detects comparisons made useless by |
35 | /// limits of the types involved. | |
36 | /// | |
37 | /// ### Example | |
38 | /// | |
39 | /// ```rust | |
40 | /// fn foo(x: u8) { | |
41 | /// x >= 0; | |
42 | /// } | |
43 | /// ``` | |
44 | /// | |
45 | /// {{produces}} | |
46 | /// | |
47 | /// ### Explanation | |
48 | /// | |
49 | /// A useless comparison may indicate a mistake, and should be fixed or | |
50 | /// removed. | |
b039eaaf SL |
51 | UNUSED_COMPARISONS, |
52 | Warn, | |
53 | "comparisons made useless by limits of the types involved" | |
54 | } | |
55 | ||
56 | declare_lint! { | |
1b1a35ee XL |
57 | /// The `overflowing_literals` lint detects literal out of range for its |
58 | /// type. | |
59 | /// | |
60 | /// ### Example | |
61 | /// | |
62 | /// ```rust,compile_fail | |
63 | /// let x: u8 = 1000; | |
64 | /// ``` | |
65 | /// | |
66 | /// {{produces}} | |
67 | /// | |
68 | /// ### Explanation | |
69 | /// | |
70 | /// It is usually a mistake to use a literal that overflows the type where | |
71 | /// it is used. Either use a literal that is within range, or change the | |
72 | /// type to be within the range of the literal. | |
b039eaaf | 73 | OVERFLOWING_LITERALS, |
9fa01778 XL |
74 | Deny, |
75 | "literal out of range for its type" | |
b039eaaf SL |
76 | } |
77 | ||
5bcae85e | 78 | declare_lint! { |
1b1a35ee XL |
79 | /// The `variant_size_differences` lint detects enums with widely varying |
80 | /// variant sizes. | |
81 | /// | |
82 | /// ### Example | |
83 | /// | |
84 | /// ```rust,compile_fail | |
85 | /// #![deny(variant_size_differences)] | |
86 | /// enum En { | |
87 | /// V0(u8), | |
88 | /// VBig([u8; 1024]), | |
89 | /// } | |
90 | /// ``` | |
91 | /// | |
92 | /// {{produces}} | |
93 | /// | |
94 | /// ### Explanation | |
95 | /// | |
96 | /// It can be a mistake to add a variant to an enum that is much larger | |
97 | /// than the other variants, bloating the overall size required for all | |
98 | /// variants. This can impact performance and memory usage. This is | |
99 | /// triggered if one variant is more than 3 times larger than the | |
100 | /// second-largest variant. | |
101 | /// | |
102 | /// Consider placing the large variant's contents on the heap (for example | |
103 | /// via [`Box`]) to keep the overall size of the enum itself down. | |
104 | /// | |
105 | /// This lint is "allow" by default because it can be noisy, and may not be | |
106 | /// an actual problem. Decisions about this should be guided with | |
107 | /// profiling and benchmarking. | |
108 | /// | |
109 | /// [`Box`]: https://doc.rust-lang.org/std/boxed/index.html | |
5bcae85e SL |
110 | VARIANT_SIZE_DIFFERENCES, |
111 | Allow, | |
112 | "detects enums with widely varying variant sizes" | |
113 | } | |
114 | ||
b039eaaf SL |
115 | #[derive(Copy, Clone)] |
116 | pub struct TypeLimits { | |
117 | /// Id of the last visited negated expression | |
ba9703b0 | 118 | negated_expr_id: Option<hir::HirId>, |
b039eaaf SL |
119 | } |
120 | ||
532ac7d7 XL |
121 | impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS]); |
122 | ||
b039eaaf SL |
123 | impl TypeLimits { |
124 | pub fn new() -> TypeLimits { | |
ba9703b0 | 125 | TypeLimits { negated_expr_id: None } |
b039eaaf SL |
126 | } |
127 | } | |
128 | ||
2b03887a FG |
129 | /// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint (`expr..MAX+1`). |
130 | /// Returns `true` iff the lint was emitted. | |
f035d41b XL |
131 | fn lint_overflowing_range_endpoint<'tcx>( |
132 | cx: &LateContext<'tcx>, | |
48663c56 XL |
133 | lit: &hir::Lit, |
134 | lit_val: u128, | |
135 | max: u128, | |
dfeec247 | 136 | expr: &'tcx hir::Expr<'tcx>, |
60c5eb7d | 137 | ty: &str, |
48663c56 XL |
138 | ) -> bool { |
139 | // We only want to handle exclusive (`..`) ranges, | |
140 | // which are represented as `ExprKind::Struct`. | |
9c376795 | 141 | let par_id = cx.tcx.hir().parent_id(expr.hir_id); |
f2b60f7d | 142 | let Node::ExprField(field) = cx.tcx.hir().get(par_id) else { return false }; |
9c376795 | 143 | let Node::Expr(struct_expr) = cx.tcx.hir().get_parent(field.hir_id) else { return false }; |
f2b60f7d FG |
144 | if !is_range_literal(struct_expr) { |
145 | return false; | |
146 | }; | |
147 | let ExprKind::Struct(_, eps, _) = &struct_expr.kind else { return false }; | |
148 | if eps.len() != 2 { | |
149 | return false; | |
150 | } | |
151 | ||
f2b60f7d FG |
152 | // We can suggest using an inclusive range |
153 | // (`..=`) instead only if it is the `end` that is | |
154 | // overflowing and only by 1. | |
2b03887a FG |
155 | if !(eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max) { |
156 | return false; | |
157 | }; | |
158 | let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) else { return false }; | |
159 | ||
9c376795 FG |
160 | use rustc_ast::{LitIntType, LitKind}; |
161 | let suffix = match lit.node { | |
162 | LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(), | |
163 | LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(), | |
164 | LitKind::Int(_, LitIntType::Unsuffixed) => "", | |
165 | _ => bug!(), | |
166 | }; | |
167 | cx.emit_spanned_lint( | |
2b03887a FG |
168 | OVERFLOWING_LITERALS, |
169 | struct_expr.span, | |
9c376795 FG |
170 | RangeEndpointOutOfRange { |
171 | ty, | |
172 | suggestion: struct_expr.span, | |
173 | start, | |
174 | literal: lit_val - 1, | |
175 | suffix, | |
2b03887a FG |
176 | }, |
177 | ); | |
178 | ||
179 | // We've just emitted a lint, special cased for `(...)..MAX+1` ranges, | |
180 | // return `true` so the callers don't also emit a lint | |
181 | true | |
48663c56 XL |
182 | } |
183 | ||
184 | // For `isize` & `usize`, be conservative with the warnings, so that the | |
185 | // warnings are consistent between 32- and 64-bit platforms. | |
5869c6ff | 186 | fn int_ty_range(int_ty: ty::IntTy) -> (i128, i128) { |
48663c56 | 187 | match int_ty { |
5869c6ff XL |
188 | ty::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()), |
189 | ty::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()), | |
190 | ty::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()), | |
191 | ty::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()), | |
192 | ty::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()), | |
193 | ty::IntTy::I128 => (i128::MIN, i128::MAX), | |
48663c56 XL |
194 | } |
195 | } | |
196 | ||
5869c6ff | 197 | fn uint_ty_range(uint_ty: ty::UintTy) -> (u128, u128) { |
29967ef6 | 198 | let max = match uint_ty { |
5869c6ff XL |
199 | ty::UintTy::Usize => u64::MAX.into(), |
200 | ty::UintTy::U8 => u8::MAX.into(), | |
201 | ty::UintTy::U16 => u16::MAX.into(), | |
202 | ty::UintTy::U32 => u32::MAX.into(), | |
203 | ty::UintTy::U64 => u64::MAX.into(), | |
204 | ty::UintTy::U128 => u128::MAX, | |
29967ef6 XL |
205 | }; |
206 | (0, max) | |
48663c56 XL |
207 | } |
208 | ||
f035d41b | 209 | fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option<String> { |
48663c56 XL |
210 | let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?; |
211 | let firstch = src.chars().next()?; | |
212 | ||
213 | if firstch == '0' { | |
214 | match src.chars().nth(1) { | |
ba9703b0 | 215 | Some('x' | 'b') => return Some(src), |
48663c56 XL |
216 | _ => return None, |
217 | } | |
218 | } | |
219 | ||
220 | None | |
221 | } | |
222 | ||
223 | fn report_bin_hex_error( | |
f035d41b | 224 | cx: &LateContext<'_>, |
dfeec247 | 225 | expr: &hir::Expr<'_>, |
48663c56 | 226 | ty: attr::IntType, |
487cf647 | 227 | size: Size, |
48663c56 XL |
228 | repr_str: String, |
229 | val: u128, | |
230 | negative: bool, | |
231 | ) { | |
9c376795 FG |
232 | let (t, actually) = match ty { |
233 | attr::IntType::SignedInt(t) => { | |
234 | let actually = if negative { | |
235 | -(size.sign_extend(val) as i128) | |
236 | } else { | |
237 | size.sign_extend(val) as i128 | |
2b03887a | 238 | }; |
9c376795 FG |
239 | (t.name_str(), actually.to_string()) |
240 | } | |
241 | attr::IntType::UnsignedInt(t) => { | |
242 | let actually = size.truncate(val); | |
243 | (t.name_str(), actually.to_string()) | |
244 | } | |
245 | }; | |
246 | let sign = | |
247 | if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive }; | |
248 | let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map( | |
249 | |suggestion_ty| { | |
250 | if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { | |
251 | let (sans_suffix, _) = repr_str.split_at(pos); | |
252 | OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix } | |
74b04a01 | 253 | } else { |
9c376795 | 254 | OverflowingBinHexSub::Help { suggestion_ty } |
2b03887a | 255 | } |
2b03887a FG |
256 | }, |
257 | ); | |
9c376795 FG |
258 | cx.emit_spanned_lint( |
259 | OVERFLOWING_LITERALS, | |
260 | expr.span, | |
261 | OverflowingBinHex { ty: t, lit: repr_str.clone(), dec: val, actually, sign, sub }, | |
262 | ) | |
48663c56 XL |
263 | } |
264 | ||
265 | // This function finds the next fitting type and generates a suggestion string. | |
266 | // It searches for fitting types in the following way (`X < Y`): | |
267 | // - `iX`: if literal fits in `uX` => `uX`, else => `iY` | |
268 | // - `-iX` => `iY` | |
269 | // - `uX` => `uY` | |
270 | // | |
271 | // No suggestion for: `isize`, `usize`. | |
60c5eb7d | 272 | fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> { |
5869c6ff XL |
273 | use ty::IntTy::*; |
274 | use ty::UintTy::*; | |
48663c56 XL |
275 | macro_rules! find_fit { |
276 | ($ty:expr, $val:expr, $negative:expr, | |
277 | $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => { | |
278 | { | |
279 | let _neg = if negative { 1 } else { 0 }; | |
280 | match $ty { | |
281 | $($type => { | |
282 | $(if !negative && val <= uint_ty_range($utypes).1 { | |
60c5eb7d | 283 | return Some($utypes.name_str()) |
48663c56 XL |
284 | })* |
285 | $(if val <= int_ty_range($itypes).1 as u128 + _neg { | |
60c5eb7d | 286 | return Some($itypes.name_str()) |
48663c56 XL |
287 | })* |
288 | None | |
dc9dc135 | 289 | },)+ |
48663c56 XL |
290 | _ => None |
291 | } | |
292 | } | |
293 | } | |
294 | } | |
1b1a35ee | 295 | match t.kind() { |
48663c56 XL |
296 | ty::Int(i) => find_fit!(i, val, negative, |
297 | I8 => [U8] => [I16, I32, I64, I128], | |
298 | I16 => [U16] => [I32, I64, I128], | |
299 | I32 => [U32] => [I64, I128], | |
300 | I64 => [U64] => [I128], | |
301 | I128 => [U128] => []), | |
302 | ty::Uint(u) => find_fit!(u, val, negative, | |
303 | U8 => [U8, U16, U32, U64, U128] => [], | |
304 | U16 => [U16, U32, U64, U128] => [], | |
305 | U32 => [U32, U64, U128] => [], | |
306 | U64 => [U64, U128] => [], | |
307 | U128 => [U128] => []), | |
308 | _ => None, | |
309 | } | |
310 | } | |
311 | ||
f035d41b XL |
312 | fn lint_int_literal<'tcx>( |
313 | cx: &LateContext<'tcx>, | |
48663c56 | 314 | type_limits: &TypeLimits, |
dfeec247 | 315 | e: &'tcx hir::Expr<'tcx>, |
48663c56 | 316 | lit: &hir::Lit, |
5869c6ff | 317 | t: ty::IntTy, |
48663c56 XL |
318 | v: u128, |
319 | ) { | |
29967ef6 | 320 | let int_type = t.normalize(cx.sess().target.pointer_width); |
ba9703b0 | 321 | let (min, max) = int_ty_range(int_type); |
48663c56 | 322 | let max = max as u128; |
ba9703b0 | 323 | let negative = type_limits.negated_expr_id == Some(e.hir_id); |
48663c56 XL |
324 | |
325 | // Detect literal value out of range [min, max] inclusive | |
326 | // avoiding use of -min to prevent overflow/panic | |
327 | if (negative && v > max + 1) || (!negative && v > max) { | |
328 | if let Some(repr_str) = get_bin_hex_repr(cx, lit) { | |
5869c6ff XL |
329 | report_bin_hex_error( |
330 | cx, | |
331 | e, | |
332 | attr::IntType::SignedInt(ty::ast_int_ty(t)), | |
487cf647 | 333 | Integer::from_int_ty(cx, t).size(), |
5869c6ff XL |
334 | repr_str, |
335 | v, | |
336 | negative, | |
337 | ); | |
48663c56 XL |
338 | return; |
339 | } | |
340 | ||
f2b60f7d | 341 | if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) { |
487cf647 | 342 | // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. |
f2b60f7d | 343 | return; |
48663c56 XL |
344 | } |
345 | ||
9c376795 FG |
346 | let lit = cx |
347 | .sess() | |
348 | .source_map() | |
349 | .span_to_snippet(lit.span) | |
350 | .expect("must get snippet from literal"); | |
351 | let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) | |
352 | .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty }); | |
353 | ||
354 | cx.emit_spanned_lint( | |
355 | OVERFLOWING_LITERALS, | |
356 | e.span, | |
357 | OverflowingInt { ty: t.name_str(), lit, min, max, help }, | |
358 | ); | |
48663c56 XL |
359 | } |
360 | } | |
361 | ||
f035d41b XL |
362 | fn lint_uint_literal<'tcx>( |
363 | cx: &LateContext<'tcx>, | |
dfeec247 | 364 | e: &'tcx hir::Expr<'tcx>, |
48663c56 | 365 | lit: &hir::Lit, |
5869c6ff | 366 | t: ty::UintTy, |
48663c56 | 367 | ) { |
29967ef6 | 368 | let uint_type = t.normalize(cx.sess().target.pointer_width); |
48663c56 XL |
369 | let (min, max) = uint_ty_range(uint_type); |
370 | let lit_val: u128 = match lit.node { | |
371 | // _v is u8, within range by definition | |
372 | ast::LitKind::Byte(_v) => return, | |
373 | ast::LitKind::Int(v, _) => v, | |
374 | _ => bug!(), | |
375 | }; | |
376 | if lit_val < min || lit_val > max { | |
9c376795 | 377 | let parent_id = cx.tcx.hir().parent_id(e.hir_id); |
dc9dc135 | 378 | if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) { |
e74abb32 | 379 | match par_e.kind { |
48663c56 | 380 | hir::ExprKind::Cast(..) => { |
1b1a35ee | 381 | if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { |
9c376795 | 382 | cx.emit_spanned_lint( |
2b03887a FG |
383 | OVERFLOWING_LITERALS, |
384 | par_e.span, | |
9c376795 | 385 | OnlyCastu8ToChar { span: par_e.span, literal: lit_val }, |
2b03887a | 386 | ); |
48663c56 XL |
387 | return; |
388 | } | |
389 | } | |
48663c56 XL |
390 | _ => {} |
391 | } | |
392 | } | |
f2b60f7d | 393 | if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) { |
487cf647 | 394 | // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. |
f2b60f7d FG |
395 | return; |
396 | } | |
48663c56 | 397 | if let Some(repr_str) = get_bin_hex_repr(cx, lit) { |
5869c6ff XL |
398 | report_bin_hex_error( |
399 | cx, | |
400 | e, | |
401 | attr::IntType::UnsignedInt(ty::ast_uint_ty(t)), | |
487cf647 | 402 | Integer::from_uint_ty(cx, t).size(), |
5869c6ff XL |
403 | repr_str, |
404 | lit_val, | |
405 | false, | |
406 | ); | |
48663c56 XL |
407 | return; |
408 | } | |
9c376795 FG |
409 | cx.emit_spanned_lint( |
410 | OVERFLOWING_LITERALS, | |
411 | e.span, | |
412 | OverflowingUInt { | |
413 | ty: t.name_str(), | |
414 | lit: cx | |
415 | .sess() | |
416 | .source_map() | |
417 | .span_to_snippet(lit.span) | |
418 | .expect("must get snippet from literal"), | |
419 | min, | |
420 | max, | |
421 | }, | |
422 | ); | |
48663c56 XL |
423 | } |
424 | } | |
425 | ||
f035d41b XL |
426 | fn lint_literal<'tcx>( |
427 | cx: &LateContext<'tcx>, | |
48663c56 | 428 | type_limits: &TypeLimits, |
dfeec247 | 429 | e: &'tcx hir::Expr<'tcx>, |
48663c56 XL |
430 | lit: &hir::Lit, |
431 | ) { | |
1b1a35ee | 432 | match *cx.typeck_results().node_type(e.hir_id).kind() { |
48663c56 XL |
433 | ty::Int(t) => { |
434 | match lit.node { | |
ba9703b0 | 435 | ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => { |
48663c56 XL |
436 | lint_int_literal(cx, type_limits, e, lit, t, v) |
437 | } | |
438 | _ => bug!(), | |
439 | }; | |
440 | } | |
dfeec247 | 441 | ty::Uint(t) => lint_uint_literal(cx, e, lit, t), |
48663c56 XL |
442 | ty::Float(t) => { |
443 | let is_infinite = match lit.node { | |
dfeec247 | 444 | ast::LitKind::Float(v, _) => match t { |
5869c6ff XL |
445 | ty::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite), |
446 | ty::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite), | |
dfeec247 | 447 | }, |
48663c56 XL |
448 | _ => bug!(), |
449 | }; | |
450 | if is_infinite == Ok(true) { | |
9c376795 | 451 | cx.emit_spanned_lint( |
2b03887a FG |
452 | OVERFLOWING_LITERALS, |
453 | e.span, | |
9c376795 FG |
454 | OverflowingLiteral { |
455 | ty: t.name_str(), | |
456 | lit: cx | |
457 | .sess() | |
458 | .source_map() | |
459 | .span_to_snippet(lit.span) | |
460 | .expect("must get snippet from literal"), | |
2b03887a FG |
461 | }, |
462 | ); | |
48663c56 XL |
463 | } |
464 | } | |
465 | _ => {} | |
466 | } | |
467 | } | |
468 | ||
f035d41b XL |
469 | impl<'tcx> LateLintPass<'tcx> for TypeLimits { |
470 | fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) { | |
e74abb32 | 471 | match e.kind { |
6a06907d | 472 | hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => { |
b039eaaf | 473 | // propagate negation, if the negation itself isn't negated |
ba9703b0 XL |
474 | if self.negated_expr_id != Some(e.hir_id) { |
475 | self.negated_expr_id = Some(expr.hir_id); | |
b039eaaf | 476 | } |
c30ab7b3 | 477 | } |
8faf50e0 | 478 | hir::ExprKind::Binary(binop, ref l, ref r) => { |
32a655c1 | 479 | if is_comparison(binop) && !check_limits(cx, binop, &l, &r) { |
9c376795 | 480 | cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons); |
b039eaaf | 481 | } |
c30ab7b3 | 482 | } |
48663c56 XL |
483 | hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit), |
484 | _ => {} | |
b039eaaf SL |
485 | }; |
486 | ||
9c376795 | 487 | fn is_valid<T: PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool { |
b039eaaf | 488 | match binop.node { |
8faf50e0 XL |
489 | hir::BinOpKind::Lt => v > min && v <= max, |
490 | hir::BinOpKind::Le => v >= min && v < max, | |
491 | hir::BinOpKind::Gt => v >= min && v < max, | |
492 | hir::BinOpKind::Ge => v > min && v <= max, | |
493 | hir::BinOpKind::Eq | hir::BinOpKind::Ne => v >= min && v <= max, | |
c30ab7b3 | 494 | _ => bug!(), |
b039eaaf SL |
495 | } |
496 | } | |
497 | ||
498 | fn rev_binop(binop: hir::BinOp) -> hir::BinOp { | |
dfeec247 XL |
499 | source_map::respan( |
500 | binop.span, | |
501 | match binop.node { | |
502 | hir::BinOpKind::Lt => hir::BinOpKind::Gt, | |
503 | hir::BinOpKind::Le => hir::BinOpKind::Ge, | |
504 | hir::BinOpKind::Gt => hir::BinOpKind::Lt, | |
505 | hir::BinOpKind::Ge => hir::BinOpKind::Le, | |
506 | _ => return binop, | |
507 | }, | |
508 | ) | |
b039eaaf SL |
509 | } |
510 | ||
dfeec247 | 511 | fn check_limits( |
f035d41b | 512 | cx: &LateContext<'_>, |
dfeec247 XL |
513 | binop: hir::BinOp, |
514 | l: &hir::Expr<'_>, | |
515 | r: &hir::Expr<'_>, | |
516 | ) -> bool { | |
e74abb32 | 517 | let (lit, expr, swap) = match (&l.kind, &r.kind) { |
8faf50e0 XL |
518 | (&hir::ExprKind::Lit(_), _) => (l, r, true), |
519 | (_, &hir::ExprKind::Lit(_)) => (r, l, false), | |
c30ab7b3 | 520 | _ => return true, |
b039eaaf SL |
521 | }; |
522 | // Normalize the binop so that the literal is always on the RHS in | |
523 | // the comparison | |
c30ab7b3 | 524 | let norm_binop = if swap { rev_binop(binop) } else { binop }; |
1b1a35ee | 525 | match *cx.typeck_results().node_type(expr.hir_id).kind() { |
b7449926 | 526 | ty::Int(int_ty) => { |
b039eaaf | 527 | let (min, max) = int_ty_range(int_ty); |
e74abb32 | 528 | let lit_val: i128 = match lit.kind { |
dfeec247 | 529 | hir::ExprKind::Lit(ref li) => match li.node { |
ba9703b0 XL |
530 | ast::LitKind::Int( |
531 | v, | |
532 | ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed, | |
533 | ) => v as i128, | |
dfeec247 | 534 | _ => return true, |
32a655c1 | 535 | }, |
dfeec247 | 536 | _ => bug!(), |
b039eaaf SL |
537 | }; |
538 | is_valid(norm_binop, lit_val, min, max) | |
539 | } | |
b7449926 | 540 | ty::Uint(uint_ty) => { |
dfeec247 | 541 | let (min, max): (u128, u128) = uint_ty_range(uint_ty); |
e74abb32 | 542 | let lit_val: u128 = match lit.kind { |
dfeec247 XL |
543 | hir::ExprKind::Lit(ref li) => match li.node { |
544 | ast::LitKind::Int(v, _) => v, | |
545 | _ => return true, | |
32a655c1 | 546 | }, |
dfeec247 | 547 | _ => bug!(), |
b039eaaf SL |
548 | }; |
549 | is_valid(norm_binop, lit_val, min, max) | |
550 | } | |
c30ab7b3 | 551 | _ => true, |
b039eaaf SL |
552 | } |
553 | } | |
554 | ||
555 | fn is_comparison(binop: hir::BinOp) -> bool { | |
29967ef6 XL |
556 | matches!( |
557 | binop.node, | |
dfeec247 | 558 | hir::BinOpKind::Eq |
29967ef6 XL |
559 | | hir::BinOpKind::Lt |
560 | | hir::BinOpKind::Le | |
561 | | hir::BinOpKind::Ne | |
562 | | hir::BinOpKind::Ge | |
563 | | hir::BinOpKind::Gt | |
564 | ) | |
b039eaaf | 565 | } |
b039eaaf SL |
566 | } |
567 | } | |
568 | ||
569 | declare_lint! { | |
1b1a35ee XL |
570 | /// The `improper_ctypes` lint detects incorrect use of types in foreign |
571 | /// modules. | |
572 | /// | |
573 | /// ### Example | |
574 | /// | |
575 | /// ```rust | |
576 | /// extern "C" { | |
577 | /// static STATIC: String; | |
578 | /// } | |
579 | /// ``` | |
580 | /// | |
581 | /// {{produces}} | |
582 | /// | |
583 | /// ### Explanation | |
584 | /// | |
585 | /// The compiler has several checks to verify that types used in `extern` | |
586 | /// blocks are safe and follow certain rules to ensure proper | |
587 | /// compatibility with the foreign interfaces. This lint is issued when it | |
588 | /// detects a probable mistake in a definition. The lint usually should | |
589 | /// provide a description of the issue, along with possibly a hint on how | |
590 | /// to resolve it. | |
b039eaaf SL |
591 | IMPROPER_CTYPES, |
592 | Warn, | |
593 | "proper use of libc types in foreign modules" | |
594 | } | |
595 | ||
f035d41b XL |
596 | declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]); |
597 | ||
598 | declare_lint! { | |
1b1a35ee XL |
599 | /// The `improper_ctypes_definitions` lint detects incorrect use of |
600 | /// [`extern` function] definitions. | |
601 | /// | |
602 | /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier | |
603 | /// | |
604 | /// ### Example | |
605 | /// | |
606 | /// ```rust | |
607 | /// # #![allow(unused)] | |
608 | /// pub extern "C" fn str_type(p: &str) { } | |
609 | /// ``` | |
610 | /// | |
611 | /// {{produces}} | |
612 | /// | |
613 | /// ### Explanation | |
614 | /// | |
615 | /// There are many parameter and return types that may be specified in an | |
616 | /// `extern` function that are not compatible with the given ABI. This | |
617 | /// lint is an alert that these types should not be used. The lint usually | |
618 | /// should provide a description of the issue, along with possibly a hint | |
619 | /// on how to resolve it. | |
f035d41b XL |
620 | IMPROPER_CTYPES_DEFINITIONS, |
621 | Warn, | |
622 | "proper use of libc types in foreign item definitions" | |
623 | } | |
624 | ||
625 | declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS]); | |
626 | ||
3dfed10e | 627 | #[derive(Clone, Copy)] |
923072b8 | 628 | pub(crate) enum CItemKind { |
3dfed10e XL |
629 | Declaration, |
630 | Definition, | |
f035d41b | 631 | } |
532ac7d7 | 632 | |
dc9dc135 | 633 | struct ImproperCTypesVisitor<'a, 'tcx> { |
f035d41b | 634 | cx: &'a LateContext<'tcx>, |
3dfed10e | 635 | mode: CItemKind, |
b039eaaf SL |
636 | } |
637 | ||
0531ce1d | 638 | enum FfiResult<'tcx> { |
b039eaaf | 639 | FfiSafe, |
0531ce1d | 640 | FfiPhantom(Ty<'tcx>), |
064997fb | 641 | FfiUnsafe { ty: Ty<'tcx>, reason: DiagnosticMessage, help: Option<DiagnosticMessage> }, |
b039eaaf SL |
642 | } |
643 | ||
923072b8 FG |
644 | pub(crate) fn nonnull_optimization_guaranteed<'tcx>( |
645 | tcx: TyCtxt<'tcx>, | |
646 | def: ty::AdtDef<'tcx>, | |
647 | ) -> bool { | |
04454e1e | 648 | tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed) |
1b1a35ee XL |
649 | } |
650 | ||
651 | /// `repr(transparent)` structs can have a single non-ZST field, this function returns that | |
652 | /// field. | |
653 | pub fn transparent_newtype_field<'a, 'tcx>( | |
654 | tcx: TyCtxt<'tcx>, | |
655 | variant: &'a ty::VariantDef, | |
656 | ) -> Option<&'a ty::FieldDef> { | |
657 | let param_env = tcx.param_env(variant.def_id); | |
17df50a5 | 658 | variant.fields.iter().find(|field| { |
9ffffee4 | 659 | let field_ty = tcx.type_of(field.did).subst_identity(); |
5869c6ff | 660 | let is_zst = tcx.layout_of(param_env.and(field_ty)).map_or(false, |layout| layout.is_zst()); |
17df50a5 XL |
661 | !is_zst |
662 | }) | |
1b1a35ee XL |
663 | } |
664 | ||
3dfed10e | 665 | /// Is type known to be non-null? |
6a06907d | 666 | fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool { |
3dfed10e | 667 | let tcx = cx.tcx; |
1b1a35ee | 668 | match ty.kind() { |
3dfed10e XL |
669 | ty::FnPtr(_) => true, |
670 | ty::Ref(..) => true, | |
671 | ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true, | |
5e7ed085 FG |
672 | ty::Adt(def, substs) if def.repr().transparent() && !def.is_union() => { |
673 | let marked_non_null = nonnull_optimization_guaranteed(tcx, *def); | |
3dfed10e | 674 | |
1b1a35ee | 675 | if marked_non_null { |
3dfed10e | 676 | return true; |
f9652781 | 677 | } |
1b1a35ee | 678 | |
064997fb FG |
679 | // `UnsafeCell` has its niche hidden. |
680 | if def.is_unsafe_cell() { | |
6a06907d XL |
681 | return false; |
682 | } | |
683 | ||
5e7ed085 | 684 | def.variants() |
17df50a5 XL |
685 | .iter() |
686 | .filter_map(|variant| transparent_newtype_field(cx.tcx, variant)) | |
687 | .any(|field| ty_is_known_nonnull(cx, field.ty(tcx, substs), mode)) | |
b039eaaf | 688 | } |
3dfed10e | 689 | _ => false, |
b039eaaf | 690 | } |
3dfed10e | 691 | } |
dc9dc135 | 692 | |
3dfed10e XL |
693 | /// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type. |
694 | /// If the type passed in was not scalar, returns None. | |
695 | fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { | |
696 | let tcx = cx.tcx; | |
1b1a35ee | 697 | Some(match *ty.kind() { |
3dfed10e XL |
698 | ty::Adt(field_def, field_substs) => { |
699 | let inner_field_ty = { | |
f2b60f7d | 700 | let mut first_non_zst_ty = field_def |
5e7ed085 FG |
701 | .variants() |
702 | .iter() | |
703 | .filter_map(|v| transparent_newtype_field(cx.tcx, v)); | |
3dfed10e XL |
704 | debug_assert_eq!( |
705 | first_non_zst_ty.clone().count(), | |
706 | 1, | |
707 | "Wrong number of fields for transparent type" | |
708 | ); | |
709 | first_non_zst_ty | |
f2b60f7d | 710 | .next_back() |
3dfed10e XL |
711 | .expect("No non-zst fields in transparent type.") |
712 | .ty(tcx, field_substs) | |
713 | }; | |
714 | return get_nullable_type(cx, inner_field_ty); | |
715 | } | |
716 | ty::Int(ty) => tcx.mk_mach_int(ty), | |
717 | ty::Uint(ty) => tcx.mk_mach_uint(ty), | |
718 | ty::RawPtr(ty_mut) => tcx.mk_ptr(ty_mut), | |
719 | // As these types are always non-null, the nullable equivalent of | |
720 | // Option<T> of these types are their raw pointer counterparts. | |
721 | ty::Ref(_region, ty, mutbl) => tcx.mk_ptr(ty::TypeAndMut { ty, mutbl }), | |
722 | ty::FnPtr(..) => { | |
723 | // There is no nullable equivalent for Rust's function pointers -- you | |
724 | // must use an Option<fn(..) -> _> to represent it. | |
725 | ty | |
726 | } | |
727 | ||
728 | // We should only ever reach this case if ty_is_known_nonnull is extended | |
729 | // to other types. | |
730 | ref unhandled => { | |
731 | debug!( | |
732 | "get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}", | |
733 | unhandled, ty | |
734 | ); | |
735 | return None; | |
736 | } | |
737 | }) | |
738 | } | |
739 | ||
740 | /// Check if this enum can be safely exported based on the "nullable pointer optimization". If it | |
1b1a35ee | 741 | /// can, return the type that `ty` can be safely converted to, otherwise return `None`. |
3dfed10e XL |
742 | /// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`, |
743 | /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. | |
744 | /// FIXME: This duplicates code in codegen. | |
923072b8 | 745 | pub(crate) fn repr_nullable_ptr<'tcx>( |
3dfed10e XL |
746 | cx: &LateContext<'tcx>, |
747 | ty: Ty<'tcx>, | |
748 | ckind: CItemKind, | |
749 | ) -> Option<Ty<'tcx>> { | |
750 | debug!("is_repr_nullable_ptr(cx, ty = {:?})", ty); | |
1b1a35ee | 751 | if let ty::Adt(ty_def, substs) = ty.kind() { |
5e7ed085 | 752 | let field_ty = match &ty_def.variants().raw[..] { |
17df50a5 XL |
753 | [var_one, var_two] => match (&var_one.fields[..], &var_two.fields[..]) { |
754 | ([], [field]) | ([field], []) => field.ty(cx.tcx, substs), | |
755 | _ => return None, | |
756 | }, | |
757 | _ => return None, | |
f035d41b | 758 | }; |
dc9dc135 | 759 | |
3dfed10e XL |
760 | if !ty_is_known_nonnull(cx, field_ty, ckind) { |
761 | return None; | |
f035d41b | 762 | } |
dc9dc135 | 763 | |
3dfed10e XL |
764 | // At this point, the field's type is known to be nonnull and the parent enum is Option-like. |
765 | // If the computed size for the field and the enum are different, the nonnull optimization isn't | |
766 | // being applied (and we've got a problem somewhere). | |
767 | let compute_size_skeleton = |t| SizeSkeleton::compute(t, cx.tcx, cx.param_env).unwrap(); | |
f035d41b XL |
768 | if !compute_size_skeleton(ty).same_size(compute_size_skeleton(field_ty)) { |
769 | bug!("improper_ctypes: Option nonnull optimization not applied?"); | |
770 | } | |
dc9dc135 | 771 | |
3dfed10e XL |
772 | // Return the nullable type this Option-like enum can be safely represented with. |
773 | let field_ty_abi = &cx.layout_of(field_ty).unwrap().abi; | |
774 | if let Abi::Scalar(field_ty_scalar) = field_ty_abi { | |
04454e1e FG |
775 | match field_ty_scalar.valid_range(cx) { |
776 | WrappingRange { start: 0, end } | |
777 | if end == field_ty_scalar.size(&cx.tcx).unsigned_int_max() - 1 => | |
778 | { | |
5e7ed085 FG |
779 | return Some(get_nullable_type(cx, field_ty).unwrap()); |
780 | } | |
04454e1e | 781 | WrappingRange { start: 1, .. } => { |
3dfed10e XL |
782 | return Some(get_nullable_type(cx, field_ty).unwrap()); |
783 | } | |
04454e1e FG |
784 | WrappingRange { start, end } => { |
785 | unreachable!("Unhandled start and end range: ({}, {})", start, end) | |
786 | } | |
3dfed10e XL |
787 | }; |
788 | } | |
f035d41b | 789 | } |
3dfed10e XL |
790 | None |
791 | } | |
b039eaaf | 792 | |
3dfed10e | 793 | impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { |
60c5eb7d XL |
794 | /// Check if the type is array and emit an unsafe type lint. |
795 | fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { | |
1b1a35ee | 796 | if let ty::Array(..) = ty.kind() { |
60c5eb7d XL |
797 | self.emit_ffi_unsafe_type_lint( |
798 | ty, | |
799 | sp, | |
2b03887a FG |
800 | fluent::lint_improper_ctypes_array_reason, |
801 | Some(fluent::lint_improper_ctypes_array_help), | |
60c5eb7d XL |
802 | ); |
803 | true | |
804 | } else { | |
805 | false | |
806 | } | |
807 | } | |
808 | ||
f035d41b XL |
809 | /// Checks if the given field's type is "ffi-safe". |
810 | fn check_field_type_for_ffi( | |
811 | &self, | |
812 | cache: &mut FxHashSet<Ty<'tcx>>, | |
813 | field: &ty::FieldDef, | |
814 | substs: SubstsRef<'tcx>, | |
815 | ) -> FfiResult<'tcx> { | |
816 | let field_ty = field.ty(self.cx.tcx, substs); | |
817 | if field_ty.has_opaque_types() { | |
818 | self.check_type_for_ffi(cache, field_ty) | |
819 | } else { | |
820 | let field_ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, field_ty); | |
821 | self.check_type_for_ffi(cache, field_ty) | |
822 | } | |
823 | } | |
824 | ||
825 | /// Checks if the given `VariantDef`'s field types are "ffi-safe". | |
826 | fn check_variant_for_ffi( | |
827 | &self, | |
828 | cache: &mut FxHashSet<Ty<'tcx>>, | |
829 | ty: Ty<'tcx>, | |
5e7ed085 | 830 | def: ty::AdtDef<'tcx>, |
f035d41b XL |
831 | variant: &ty::VariantDef, |
832 | substs: SubstsRef<'tcx>, | |
833 | ) -> FfiResult<'tcx> { | |
834 | use FfiResult::*; | |
835 | ||
9c376795 | 836 | let transparent_safety = def.repr().transparent().then(|| { |
c295e0f8 | 837 | // Can assume that at most one field is not a ZST, so only check |
f035d41b | 838 | // that field's type for FFI-safety. |
1b1a35ee | 839 | if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) { |
9c376795 | 840 | return self.check_field_type_for_ffi(cache, field, substs); |
f035d41b | 841 | } else { |
c295e0f8 | 842 | // All fields are ZSTs; this means that the type should behave |
9c376795 FG |
843 | // like (), which is FFI-unsafe... except if all fields are PhantomData, |
844 | // which is tested for below | |
2b03887a | 845 | FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } |
f035d41b | 846 | } |
9c376795 FG |
847 | }); |
848 | // We can't completely trust repr(C) markings; make sure the fields are | |
849 | // actually safe. | |
850 | let mut all_phantom = !variant.fields.is_empty(); | |
851 | for field in &variant.fields { | |
852 | match self.check_field_type_for_ffi(cache, &field, substs) { | |
853 | FfiSafe => { | |
854 | all_phantom = false; | |
855 | } | |
856 | FfiPhantom(..) if !def.repr().transparent() && def.is_enum() => { | |
857 | return FfiUnsafe { | |
858 | ty, | |
859 | reason: fluent::lint_improper_ctypes_enum_phantomdata, | |
860 | help: None, | |
861 | }; | |
f035d41b | 862 | } |
9c376795 FG |
863 | FfiPhantom(..) => {} |
864 | r => return transparent_safety.unwrap_or(r), | |
f035d41b | 865 | } |
f035d41b | 866 | } |
9c376795 FG |
867 | |
868 | if all_phantom { FfiPhantom(ty) } else { transparent_safety.unwrap_or(FfiSafe) } | |
f035d41b XL |
869 | } |
870 | ||
9fa01778 | 871 | /// Checks if the given type is "ffi-safe" (has a stable, well-defined |
b039eaaf | 872 | /// representation which can be exported to C code). |
dfeec247 | 873 | fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult<'tcx> { |
9fa01778 | 874 | use FfiResult::*; |
8bb4bdeb | 875 | |
3dfed10e | 876 | let tcx = self.cx.tcx; |
b039eaaf SL |
877 | |
878 | // Protect against infinite recursion, for example | |
879 | // `struct S(*mut S);`. | |
880 | // FIXME: A recursion limit is necessary as well, for irregular | |
b7449926 | 881 | // recursive types. |
b039eaaf SL |
882 | if !cache.insert(ty) { |
883 | return FfiSafe; | |
884 | } | |
885 | ||
fc512014 | 886 | match *ty.kind() { |
b7449926 | 887 | ty::Adt(def, substs) => { |
17df50a5 | 888 | if def.is_box() && matches!(self.mode, CItemKind::Definition) { |
2b03887a | 889 | if ty.boxed_ty().is_sized(tcx, self.cx.param_env) { |
17df50a5 XL |
890 | return FfiSafe; |
891 | } else { | |
892 | return FfiUnsafe { | |
893 | ty, | |
2b03887a | 894 | reason: fluent::lint_improper_ctypes_box, |
17df50a5 XL |
895 | help: None, |
896 | }; | |
897 | } | |
898 | } | |
8bb4bdeb | 899 | if def.is_phantom_data() { |
0531ce1d | 900 | return FfiPhantom(ty); |
8bb4bdeb | 901 | } |
c30ab7b3 | 902 | match def.adt_kind() { |
f035d41b | 903 | AdtKind::Struct | AdtKind::Union => { |
5e7ed085 | 904 | if !def.repr().c() && !def.repr().transparent() { |
0531ce1d | 905 | return FfiUnsafe { |
e1599b0c | 906 | ty, |
064997fb | 907 | reason: if def.is_struct() { |
2b03887a | 908 | fluent::lint_improper_ctypes_struct_layout_reason |
064997fb | 909 | } else { |
2b03887a | 910 | fluent::lint_improper_ctypes_union_layout_reason |
064997fb FG |
911 | }, |
912 | help: if def.is_struct() { | |
2b03887a | 913 | Some(fluent::lint_improper_ctypes_struct_layout_help) |
064997fb | 914 | } else { |
2b03887a | 915 | Some(fluent::lint_improper_ctypes_union_layout_help) |
064997fb | 916 | }, |
0531ce1d | 917 | }; |
c30ab7b3 | 918 | } |
b039eaaf | 919 | |
e74abb32 XL |
920 | let is_non_exhaustive = |
921 | def.non_enum_variant().is_field_list_non_exhaustive(); | |
5e7ed085 | 922 | if is_non_exhaustive && !def.did().is_local() { |
e74abb32 XL |
923 | return FfiUnsafe { |
924 | ty, | |
064997fb | 925 | reason: if def.is_struct() { |
2b03887a | 926 | fluent::lint_improper_ctypes_struct_non_exhaustive |
064997fb | 927 | } else { |
2b03887a | 928 | fluent::lint_improper_ctypes_union_non_exhaustive |
064997fb | 929 | }, |
e74abb32 XL |
930 | help: None, |
931 | }; | |
932 | } | |
933 | ||
2c00a5a8 | 934 | if def.non_enum_variant().fields.is_empty() { |
0531ce1d | 935 | return FfiUnsafe { |
e1599b0c | 936 | ty, |
064997fb | 937 | reason: if def.is_struct() { |
2b03887a | 938 | fluent::lint_improper_ctypes_struct_fieldless_reason |
064997fb | 939 | } else { |
2b03887a | 940 | fluent::lint_improper_ctypes_union_fieldless_reason |
064997fb FG |
941 | }, |
942 | help: if def.is_struct() { | |
2b03887a | 943 | Some(fluent::lint_improper_ctypes_struct_fieldless_help) |
064997fb | 944 | } else { |
2b03887a | 945 | Some(fluent::lint_improper_ctypes_union_fieldless_help) |
064997fb | 946 | }, |
0531ce1d | 947 | }; |
8bb4bdeb XL |
948 | } |
949 | ||
f035d41b | 950 | self.check_variant_for_ffi(cache, ty, def, def.non_enum_variant(), substs) |
b039eaaf | 951 | } |
c30ab7b3 | 952 | AdtKind::Enum => { |
5e7ed085 | 953 | if def.variants().is_empty() { |
c30ab7b3 SL |
954 | // Empty enums are okay... although sort of useless. |
955 | return FfiSafe; | |
956 | } | |
9e0c209e | 957 | |
c30ab7b3 SL |
958 | // Check for a repr() attribute to specify the size of the |
959 | // discriminant. | |
5e7ed085 FG |
960 | if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() |
961 | { | |
8bb4bdeb | 962 | // Special-case types like `Option<extern fn()>`. |
3dfed10e | 963 | if repr_nullable_ptr(self.cx, ty, self.mode).is_none() { |
0531ce1d | 964 | return FfiUnsafe { |
e1599b0c | 965 | ty, |
2b03887a FG |
966 | reason: fluent::lint_improper_ctypes_enum_repr_reason, |
967 | help: Some(fluent::lint_improper_ctypes_enum_repr_help), | |
0531ce1d | 968 | }; |
9e0c209e | 969 | } |
8bb4bdeb | 970 | } |
c30ab7b3 | 971 | |
5e7ed085 | 972 | if def.is_variant_list_non_exhaustive() && !def.did().is_local() { |
e74abb32 XL |
973 | return FfiUnsafe { |
974 | ty, | |
2b03887a | 975 | reason: fluent::lint_improper_ctypes_non_exhaustive, |
e74abb32 XL |
976 | help: None, |
977 | }; | |
978 | } | |
979 | ||
c30ab7b3 | 980 | // Check the contained variants. |
5e7ed085 | 981 | for variant in def.variants() { |
e74abb32 XL |
982 | let is_non_exhaustive = variant.is_field_list_non_exhaustive(); |
983 | if is_non_exhaustive && !variant.def_id.is_local() { | |
984 | return FfiUnsafe { | |
985 | ty, | |
2b03887a | 986 | reason: fluent::lint_improper_ctypes_non_exhaustive_variant, |
e74abb32 XL |
987 | help: None, |
988 | }; | |
989 | } | |
990 | ||
f035d41b XL |
991 | match self.check_variant_for_ffi(cache, ty, def, variant, substs) { |
992 | FfiSafe => (), | |
993 | r => return r, | |
9e0c209e | 994 | } |
b039eaaf | 995 | } |
f035d41b | 996 | |
c30ab7b3 | 997 | FfiSafe |
b039eaaf SL |
998 | } |
999 | } | |
c30ab7b3 | 1000 | } |
b039eaaf | 1001 | |
b7449926 | 1002 | ty::Char => FfiUnsafe { |
e1599b0c | 1003 | ty, |
2b03887a FG |
1004 | reason: fluent::lint_improper_ctypes_char_reason, |
1005 | help: Some(fluent::lint_improper_ctypes_char_help), | |
0531ce1d | 1006 | }, |
b039eaaf | 1007 | |
064997fb | 1008 | ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => { |
2b03887a | 1009 | FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None } |
064997fb | 1010 | } |
ea8adc8c | 1011 | |
b039eaaf | 1012 | // Primitive types with a stable representation. |
b7449926 | 1013 | ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe, |
b039eaaf | 1014 | |
b7449926 | 1015 | ty::Slice(_) => FfiUnsafe { |
e1599b0c | 1016 | ty, |
2b03887a FG |
1017 | reason: fluent::lint_improper_ctypes_slice_reason, |
1018 | help: Some(fluent::lint_improper_ctypes_slice_help), | |
0531ce1d XL |
1019 | }, |
1020 | ||
dfeec247 | 1021 | ty::Dynamic(..) => { |
2b03887a | 1022 | FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None } |
dfeec247 | 1023 | } |
0531ce1d | 1024 | |
b7449926 | 1025 | ty::Str => FfiUnsafe { |
e1599b0c | 1026 | ty, |
2b03887a FG |
1027 | reason: fluent::lint_improper_ctypes_str_reason, |
1028 | help: Some(fluent::lint_improper_ctypes_str_help), | |
0531ce1d XL |
1029 | }, |
1030 | ||
b7449926 | 1031 | ty::Tuple(..) => FfiUnsafe { |
e1599b0c | 1032 | ty, |
2b03887a FG |
1033 | reason: fluent::lint_improper_ctypes_tuple_reason, |
1034 | help: Some(fluent::lint_improper_ctypes_tuple_help), | |
0531ce1d | 1035 | }, |
b039eaaf | 1036 | |
f035d41b XL |
1037 | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) |
1038 | if { | |
3dfed10e | 1039 | matches!(self.mode, CItemKind::Definition) |
2b03887a | 1040 | && ty.is_sized(self.cx.tcx, self.cx.param_env) |
f035d41b XL |
1041 | } => |
1042 | { | |
1043 | FfiSafe | |
1044 | } | |
1045 | ||
c295e0f8 XL |
1046 | ty::RawPtr(ty::TypeAndMut { ty, .. }) |
1047 | if match ty.kind() { | |
1048 | ty::Tuple(tuple) => tuple.is_empty(), | |
1049 | _ => false, | |
1050 | } => | |
1051 | { | |
1052 | FfiSafe | |
1053 | } | |
1054 | ||
dfeec247 XL |
1055 | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => { |
1056 | self.check_type_for_ffi(cache, ty) | |
1057 | } | |
b039eaaf | 1058 | |
60c5eb7d | 1059 | ty::Array(inner_ty, _) => self.check_type_for_ffi(cache, inner_ty), |
b039eaaf | 1060 | |
b7449926 | 1061 | ty::FnPtr(sig) => { |
f035d41b XL |
1062 | if self.is_internal_abi(sig.abi()) { |
1063 | return FfiUnsafe { | |
1064 | ty, | |
2b03887a FG |
1065 | reason: fluent::lint_improper_ctypes_fnptr_reason, |
1066 | help: Some(fluent::lint_improper_ctypes_fnptr_help), | |
f035d41b | 1067 | }; |
b039eaaf SL |
1068 | } |
1069 | ||
fc512014 | 1070 | let sig = tcx.erase_late_bound_regions(sig); |
b7449926 | 1071 | if !sig.output().is_unit() { |
476ff2be | 1072 | let r = self.check_type_for_ffi(cache, sig.output()); |
5bcae85e SL |
1073 | match r { |
1074 | FfiSafe => {} | |
c30ab7b3 SL |
1075 | _ => { |
1076 | return r; | |
1077 | } | |
b039eaaf SL |
1078 | } |
1079 | } | |
476ff2be | 1080 | for arg in sig.inputs() { |
5099ac24 | 1081 | let r = self.check_type_for_ffi(cache, *arg); |
b039eaaf SL |
1082 | match r { |
1083 | FfiSafe => {} | |
c30ab7b3 SL |
1084 | _ => { |
1085 | return r; | |
1086 | } | |
b039eaaf SL |
1087 | } |
1088 | } | |
1089 | FfiSafe | |
1090 | } | |
1091 | ||
b7449926 XL |
1092 | ty::Foreign(..) => FfiSafe, |
1093 | ||
f035d41b XL |
1094 | // While opaque types are checked for earlier, if a projection in a struct field |
1095 | // normalizes to an opaque type, then it will reach this branch. | |
9c376795 | 1096 | ty::Alias(ty::Opaque, ..) => { |
2b03887a | 1097 | FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None } |
f035d41b XL |
1098 | } |
1099 | ||
1100 | // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe, | |
1101 | // so they are currently ignored for the purposes of this lint. | |
9c376795 FG |
1102 | ty::Param(..) | ty::Alias(ty::Projection, ..) |
1103 | if matches!(self.mode, CItemKind::Definition) => | |
1104 | { | |
f035d41b XL |
1105 | FfiSafe |
1106 | } | |
1107 | ||
dfeec247 | 1108 | ty::Param(..) |
9c376795 | 1109 | | ty::Alias(ty::Projection, ..) |
dfeec247 XL |
1110 | | ty::Infer(..) |
1111 | | ty::Bound(..) | |
f035d41b | 1112 | | ty::Error(_) |
dfeec247 XL |
1113 | | ty::Closure(..) |
1114 | | ty::Generator(..) | |
1115 | | ty::GeneratorWitness(..) | |
9ffffee4 | 1116 | | ty::GeneratorWitnessMIR(..) |
dfeec247 | 1117 | | ty::Placeholder(..) |
dfeec247 | 1118 | | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty), |
e1599b0c XL |
1119 | } |
1120 | } | |
1121 | ||
1122 | fn emit_ffi_unsafe_type_lint( | |
1123 | &mut self, | |
1124 | ty: Ty<'tcx>, | |
1125 | sp: Span, | |
064997fb FG |
1126 | note: DiagnosticMessage, |
1127 | help: Option<DiagnosticMessage>, | |
e1599b0c | 1128 | ) { |
f035d41b | 1129 | let lint = match self.mode { |
3dfed10e XL |
1130 | CItemKind::Declaration => IMPROPER_CTYPES, |
1131 | CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS, | |
f035d41b | 1132 | }; |
9c376795 FG |
1133 | let desc = match self.mode { |
1134 | CItemKind::Declaration => "block", | |
1135 | CItemKind::Definition => "fn", | |
1136 | }; | |
1137 | let span_note = if let ty::Adt(def, _) = ty.kind() | |
1138 | && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) { | |
1139 | Some(sp) | |
1140 | } else { | |
1141 | None | |
f035d41b | 1142 | }; |
9c376795 FG |
1143 | self.cx.emit_spanned_lint( |
1144 | lint, | |
1145 | sp, | |
1146 | ImproperCTypes { ty, desc, label: sp, help, note, span_note }, | |
1147 | ); | |
e1599b0c XL |
1148 | } |
1149 | ||
1150 | fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { | |
487cf647 | 1151 | struct ProhibitOpaqueTypes; |
9ffffee4 | 1152 | impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes { |
fc512014 XL |
1153 | type BreakTy = Ty<'tcx>; |
1154 | ||
1155 | fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { | |
487cf647 | 1156 | if !ty.has_opaque_types() { |
9c376795 | 1157 | return ControlFlow::Continue(()); |
487cf647 FG |
1158 | } |
1159 | ||
9c376795 | 1160 | if let ty::Alias(ty::Opaque, ..) = ty.kind() { |
487cf647 FG |
1161 | ControlFlow::Break(ty) |
1162 | } else { | |
1163 | ty.super_visit_with(self) | |
e1599b0c XL |
1164 | } |
1165 | } | |
1166 | } | |
1167 | ||
487cf647 FG |
1168 | if let Some(ty) = self |
1169 | .cx | |
1170 | .tcx | |
1171 | .normalize_erasing_regions(self.cx.param_env, ty) | |
1172 | .visit_with(&mut ProhibitOpaqueTypes) | |
1173 | .break_value() | |
1174 | { | |
2b03887a | 1175 | self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None); |
e1599b0c XL |
1176 | true |
1177 | } else { | |
1178 | false | |
b039eaaf SL |
1179 | } |
1180 | } | |
1181 | ||
f035d41b XL |
1182 | fn check_type_for_ffi_and_report_errors( |
1183 | &mut self, | |
1184 | sp: Span, | |
1185 | ty: Ty<'tcx>, | |
1186 | is_static: bool, | |
1187 | is_return_type: bool, | |
1188 | ) { | |
e1599b0c XL |
1189 | // We have to check for opaque types before `normalize_erasing_regions`, |
1190 | // which will replace opaque types with their underlying concrete type. | |
1191 | if self.check_for_opaque_ty(sp, ty) { | |
1192 | // We've already emitted an error due to an opaque type. | |
1193 | return; | |
1194 | } | |
1195 | ||
54a0048b SL |
1196 | // it is only OK to use this function because extern fns cannot have |
1197 | // any generic types right now: | |
f035d41b XL |
1198 | let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty); |
1199 | ||
1200 | // C doesn't really support passing arrays by value - the only way to pass an array by value | |
1201 | // is through a struct. So, first test that the top level isn't an array, and then | |
1202 | // recursively check the types inside. | |
60c5eb7d XL |
1203 | if !is_static && self.check_for_array_ty(sp, ty) { |
1204 | return; | |
1205 | } | |
b039eaaf | 1206 | |
f035d41b XL |
1207 | // Don't report FFI errors for unit return types. This check exists here, and not in |
1208 | // `check_foreign_fn` (where it would make more sense) so that normalization has definitely | |
1209 | // happened. | |
1210 | if is_return_type && ty.is_unit() { | |
1211 | return; | |
1212 | } | |
1213 | ||
0bf4aa26 | 1214 | match self.check_type_for_ffi(&mut FxHashSet::default(), ty) { |
b039eaaf | 1215 | FfiResult::FfiSafe => {} |
0531ce1d | 1216 | FfiResult::FfiPhantom(ty) => { |
064997fb FG |
1217 | self.emit_ffi_unsafe_type_lint( |
1218 | ty, | |
1219 | sp, | |
2b03887a | 1220 | fluent::lint_improper_ctypes_only_phantomdata, |
064997fb FG |
1221 | None, |
1222 | ); | |
9e0c209e | 1223 | } |
f035d41b XL |
1224 | // If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic |
1225 | // argument, which after substitution, is `()`, then this branch can be hit. | |
3dfed10e | 1226 | FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => {} |
e1599b0c | 1227 | FfiResult::FfiUnsafe { ty, reason, help } => { |
064997fb | 1228 | self.emit_ffi_unsafe_type_lint(ty, sp, reason, help); |
b039eaaf SL |
1229 | } |
1230 | } | |
1231 | } | |
b039eaaf | 1232 | |
9ffffee4 FG |
1233 | fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &hir::FnDecl<'_>) { |
1234 | let sig = self.cx.tcx.fn_sig(def_id).subst_identity(); | |
fc512014 | 1235 | let sig = self.cx.tcx.erase_late_bound_regions(sig); |
54a0048b | 1236 | |
cdc7bbd5 | 1237 | for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { |
5099ac24 | 1238 | self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false); |
54a0048b SL |
1239 | } |
1240 | ||
74b04a01 | 1241 | if let hir::FnRetTy::Return(ref ret_hir) = decl.output { |
476ff2be | 1242 | let ret_ty = sig.output(); |
f035d41b | 1243 | self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty, false, true); |
b039eaaf SL |
1244 | } |
1245 | } | |
54a0048b | 1246 | |
9ffffee4 FG |
1247 | fn check_foreign_static(&mut self, id: hir::OwnerId, span: Span) { |
1248 | let ty = self.cx.tcx.type_of(id).subst_identity(); | |
f035d41b | 1249 | self.check_type_for_ffi_and_report_errors(span, ty, true, false); |
54a0048b | 1250 | } |
b039eaaf | 1251 | |
3dfed10e | 1252 | fn is_internal_abi(&self, abi: SpecAbi) -> bool { |
29967ef6 XL |
1253 | matches!( |
1254 | abi, | |
1255 | SpecAbi::Rust | SpecAbi::RustCall | SpecAbi::RustIntrinsic | SpecAbi::PlatformIntrinsic | |
1256 | ) | |
f035d41b XL |
1257 | } |
1258 | } | |
1259 | ||
1260 | impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { | |
1261 | fn check_foreign_item(&mut self, cx: &LateContext<'_>, it: &hir::ForeignItem<'_>) { | |
3dfed10e | 1262 | let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration }; |
6a06907d | 1263 | let abi = cx.tcx.hir().get_foreign_abi(it.hir_id()); |
f035d41b XL |
1264 | |
1265 | if !vis.is_internal_abi(abi) { | |
e74abb32 | 1266 | match it.kind { |
b7449926 | 1267 | hir::ForeignItemKind::Fn(ref decl, _, _) => { |
9ffffee4 | 1268 | vis.check_foreign_fn(it.owner_id.def_id, decl); |
b7449926 XL |
1269 | } |
1270 | hir::ForeignItemKind::Static(ref ty, _) => { | |
9ffffee4 | 1271 | vis.check_foreign_static(it.owner_id, ty.span); |
b039eaaf | 1272 | } |
dfeec247 | 1273 | hir::ForeignItemKind::Type => (), |
b039eaaf | 1274 | } |
b039eaaf SL |
1275 | } |
1276 | } | |
1277 | } | |
5bcae85e | 1278 | |
f035d41b XL |
1279 | impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { |
1280 | fn check_fn( | |
1281 | &mut self, | |
1282 | cx: &LateContext<'tcx>, | |
1283 | kind: hir::intravisit::FnKind<'tcx>, | |
1284 | decl: &'tcx hir::FnDecl<'_>, | |
1285 | _: &'tcx hir::Body<'_>, | |
1286 | _: Span, | |
9ffffee4 | 1287 | id: LocalDefId, |
f035d41b XL |
1288 | ) { |
1289 | use hir::intravisit::FnKind; | |
1290 | ||
1291 | let abi = match kind { | |
1292 | FnKind::ItemFn(_, _, header, ..) => header.abi, | |
1293 | FnKind::Method(_, sig, ..) => sig.header.abi, | |
1294 | _ => return, | |
1295 | }; | |
1296 | ||
3dfed10e | 1297 | let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition }; |
f035d41b | 1298 | if !vis.is_internal_abi(abi) { |
9ffffee4 | 1299 | vis.check_foreign_fn(id, decl); |
f035d41b XL |
1300 | } |
1301 | } | |
1302 | } | |
1303 | ||
532ac7d7 | 1304 | declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]); |
5bcae85e | 1305 | |
f035d41b XL |
1306 | impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences { |
1307 | fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { | |
e74abb32 | 1308 | if let hir::ItemKind::Enum(ref enum_definition, _) = it.kind { |
9ffffee4 | 1309 | let t = cx.tcx.type_of(it.owner_id).subst_identity(); |
fc512014 | 1310 | let ty = cx.tcx.erase_regions(t); |
5e7ed085 | 1311 | let Ok(layout) = cx.layout_of(ty) else { return }; |
a2a8927a | 1312 | let Variants::Multiple { |
c295e0f8 | 1313 | tag_encoding: TagEncoding::Direct, tag, ref variants, .. |
a2a8927a XL |
1314 | } = &layout.variants else { |
1315 | return | |
532ac7d7 XL |
1316 | }; |
1317 | ||
04454e1e | 1318 | let tag_size = tag.size(&cx.tcx).bytes(); |
532ac7d7 | 1319 | |
dfeec247 XL |
1320 | debug!( |
1321 | "enum `{}` is {} bytes large with layout:\n{:#?}", | |
1322 | t, | |
1323 | layout.size.bytes(), | |
1324 | layout | |
1325 | ); | |
532ac7d7 | 1326 | |
cdc7bbd5 | 1327 | let (largest, slargest, largest_index) = iter::zip(enum_definition.variants, variants) |
532ac7d7 | 1328 | .map(|(variant, variant_layout)| { |
f035d41b | 1329 | // Subtract the size of the enum tag. |
487cf647 | 1330 | let bytes = variant_layout.size.bytes().saturating_sub(tag_size); |
532ac7d7 | 1331 | |
dfeec247 | 1332 | debug!("- variant `{}` is {} bytes large", variant.ident, bytes); |
532ac7d7 XL |
1333 | bytes |
1334 | }) | |
1335 | .enumerate() | |
dfeec247 XL |
1336 | .fold((0, 0, 0), |(l, s, li), (idx, size)| { |
1337 | if size > l { | |
1338 | (size, l, idx) | |
1339 | } else if size > s { | |
1340 | (l, size, li) | |
1341 | } else { | |
1342 | (l, s, li) | |
1343 | } | |
532ac7d7 XL |
1344 | }); |
1345 | ||
1346 | // We only warn if the largest variant is at least thrice as large as | |
1347 | // the second-largest. | |
1348 | if largest > slargest * 3 && slargest > 0 { | |
9c376795 | 1349 | cx.emit_spanned_lint( |
dfeec247 XL |
1350 | VARIANT_SIZE_DIFFERENCES, |
1351 | enum_definition.variants[largest_index].span, | |
9c376795 | 1352 | VariantSizeDifferencesDiag { largest }, |
dfeec247 | 1353 | ); |
5bcae85e SL |
1354 | } |
1355 | } | |
1356 | } | |
1357 | } | |
94222f64 XL |
1358 | |
1359 | declare_lint! { | |
1360 | /// The `invalid_atomic_ordering` lint detects passing an `Ordering` | |
1361 | /// to an atomic operation that does not support that ordering. | |
1362 | /// | |
1363 | /// ### Example | |
1364 | /// | |
1365 | /// ```rust,compile_fail | |
1366 | /// # use core::sync::atomic::{AtomicU8, Ordering}; | |
1367 | /// let atom = AtomicU8::new(0); | |
1368 | /// let value = atom.load(Ordering::Release); | |
1369 | /// # let _ = value; | |
1370 | /// ``` | |
1371 | /// | |
1372 | /// {{produces}} | |
1373 | /// | |
1374 | /// ### Explanation | |
1375 | /// | |
1376 | /// Some atomic operations are only supported for a subset of the | |
1377 | /// `atomic::Ordering` variants. Passing an unsupported variant will cause | |
1378 | /// an unconditional panic at runtime, which is detected by this lint. | |
1379 | /// | |
1380 | /// This lint will trigger in the following cases: (where `AtomicType` is an | |
1381 | /// atomic type from `core::sync::atomic`, such as `AtomicBool`, | |
1382 | /// `AtomicPtr`, `AtomicUsize`, or any of the other integer atomics). | |
1383 | /// | |
1384 | /// - Passing `Ordering::Acquire` or `Ordering::AcqRel` to | |
1385 | /// `AtomicType::store`. | |
1386 | /// | |
1387 | /// - Passing `Ordering::Release` or `Ordering::AcqRel` to | |
1388 | /// `AtomicType::load`. | |
1389 | /// | |
1390 | /// - Passing `Ordering::Relaxed` to `core::sync::atomic::fence` or | |
1391 | /// `core::sync::atomic::compiler_fence`. | |
1392 | /// | |
1393 | /// - Passing `Ordering::Release` or `Ordering::AcqRel` as the failure | |
1394 | /// ordering for any of `AtomicType::compare_exchange`, | |
1395 | /// `AtomicType::compare_exchange_weak`, or `AtomicType::fetch_update`. | |
94222f64 XL |
1396 | INVALID_ATOMIC_ORDERING, |
1397 | Deny, | |
1398 | "usage of invalid atomic ordering in atomic operations and memory fences" | |
1399 | } | |
1400 | ||
1401 | declare_lint_pass!(InvalidAtomicOrdering => [INVALID_ATOMIC_ORDERING]); | |
1402 | ||
1403 | impl InvalidAtomicOrdering { | |
1404 | fn inherent_atomic_method_call<'hir>( | |
1405 | cx: &LateContext<'_>, | |
1406 | expr: &Expr<'hir>, | |
1407 | recognized_names: &[Symbol], // used for fast path calculation | |
1408 | ) -> Option<(Symbol, &'hir [Expr<'hir>])> { | |
1409 | const ATOMIC_TYPES: &[Symbol] = &[ | |
1410 | sym::AtomicBool, | |
1411 | sym::AtomicPtr, | |
1412 | sym::AtomicUsize, | |
1413 | sym::AtomicU8, | |
1414 | sym::AtomicU16, | |
1415 | sym::AtomicU32, | |
1416 | sym::AtomicU64, | |
1417 | sym::AtomicU128, | |
1418 | sym::AtomicIsize, | |
1419 | sym::AtomicI8, | |
1420 | sym::AtomicI16, | |
1421 | sym::AtomicI32, | |
1422 | sym::AtomicI64, | |
1423 | sym::AtomicI128, | |
1424 | ]; | |
f2b60f7d | 1425 | if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind |
5e7ed085 FG |
1426 | && recognized_names.contains(&method_path.ident.name) |
1427 | && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) | |
1428 | && let Some(impl_did) = cx.tcx.impl_of_method(m_def_id) | |
9ffffee4 | 1429 | && let Some(adt) = cx.tcx.type_of(impl_did).subst_identity().ty_adt_def() |
94222f64 | 1430 | // skip extension traits, only lint functions from the standard library |
5e7ed085 | 1431 | && cx.tcx.trait_id_of_impl(impl_did).is_none() |
04454e1e | 1432 | && let parent = cx.tcx.parent(adt.did()) |
5e7ed085 FG |
1433 | && cx.tcx.is_diagnostic_item(sym::atomic_mod, parent) |
1434 | && ATOMIC_TYPES.contains(&cx.tcx.item_name(adt.did())) | |
1435 | { | |
1436 | return Some((method_path.ident.name, args)); | |
94222f64 XL |
1437 | } |
1438 | None | |
1439 | } | |
1440 | ||
064997fb FG |
1441 | fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol> { |
1442 | let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None }; | |
1443 | let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?; | |
94222f64 XL |
1444 | let tcx = cx.tcx; |
1445 | let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering); | |
064997fb FG |
1446 | let name = tcx.item_name(did); |
1447 | let parent = tcx.parent(did); | |
1448 | [sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find( | |
1449 | |&ordering| { | |
1450 | name == ordering | |
1451 | && (Some(parent) == atomic_ordering | |
1452 | // needed in case this is a ctor, not a variant | |
1453 | || tcx.opt_parent(parent) == atomic_ordering) | |
1454 | }, | |
1455 | ) | |
94222f64 XL |
1456 | } |
1457 | ||
1458 | fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) { | |
5e7ed085 | 1459 | if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store]) |
9c376795 FG |
1460 | && let Some((ordering_arg, invalid_ordering)) = match method { |
1461 | sym::load => Some((&args[0], sym::Release)), | |
1462 | sym::store => Some((&args[1], sym::Acquire)), | |
94222f64 | 1463 | _ => None, |
94222f64 | 1464 | } |
064997fb FG |
1465 | && let Some(ordering) = Self::match_ordering(cx, ordering_arg) |
1466 | && (ordering == invalid_ordering || ordering == sym::AcqRel) | |
5e7ed085 | 1467 | { |
9c376795 FG |
1468 | if method == sym::load { |
1469 | cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad); | |
1470 | } else { | |
1471 | cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore); | |
1472 | }; | |
94222f64 XL |
1473 | } |
1474 | } | |
1475 | ||
1476 | fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) { | |
5e7ed085 FG |
1477 | if let ExprKind::Call(ref func, ref args) = expr.kind |
1478 | && let ExprKind::Path(ref func_qpath) = func.kind | |
1479 | && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() | |
1480 | && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence)) | |
064997fb | 1481 | && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed) |
5e7ed085 | 1482 | { |
9c376795 | 1483 | cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence); |
94222f64 XL |
1484 | } |
1485 | } | |
1486 | ||
1487 | fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { | |
064997fb FG |
1488 | let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak]) |
1489 | else {return }; | |
1490 | ||
1491 | let fail_order_arg = match method { | |
f2b60f7d FG |
1492 | sym::fetch_update => &args[1], |
1493 | sym::compare_exchange | sym::compare_exchange_weak => &args[3], | |
064997fb FG |
1494 | _ => return, |
1495 | }; | |
1496 | ||
1497 | let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return }; | |
1498 | ||
1499 | if matches!(fail_ordering, sym::Release | sym::AcqRel) { | |
064997fb FG |
1500 | cx.emit_spanned_lint( |
1501 | INVALID_ATOMIC_ORDERING, | |
1502 | fail_order_arg.span, | |
1503 | InvalidAtomicOrderingDiag { method, fail_order_arg_span: fail_order_arg.span }, | |
1504 | ); | |
94222f64 XL |
1505 | } |
1506 | } | |
1507 | } | |
1508 | ||
1509 | impl<'tcx> LateLintPass<'tcx> for InvalidAtomicOrdering { | |
1510 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | |
1511 | Self::check_atomic_load_store(cx, expr); | |
1512 | Self::check_memory_fence(cx, expr); | |
1513 | Self::check_atomic_compare_exchange(cx, expr); | |
1514 | } | |
1515 | } |