1use std::iter;
2
3use rustc_abi::{BackendRepr, TagEncoding, Variants, WrappingRange};
4use rustc_ast as ast;
5use rustc_hir as hir;
6use rustc_hir::{Expr, ExprKind, HirId, LangItem, find_attr};
7use rustc_middle::bug;
8use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton};
9use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Unnormalized};
10use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
11use rustc_span::{DUMMY_SP, Span, Symbol, sym};
12use tracing::debug;
13
14mod improper_ctypes; pub(crate) use improper_ctypes::ImproperCTypesLint;
16
17pub(crate) use crate::ferrocene::LintUnvalidated;
19use crate::lints::{
20 AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
21 AmbiguousWidePointerComparisonsAddrSuggestion, AmbiguousWidePointerComparisonsCastSuggestion,
22 AmbiguousWidePointerComparisonsExpectSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
23 AtomicOrderingStore, InvalidAtomicOrderingDiag, InvalidNanComparisons,
24 InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
25 UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons,
26 VariantSizeDifferencesDiag,
27};
28use crate::{LateContext, LateLintPass, LintContext};
29
30mod literal;
31use literal::{int_ty_range, lint_literal, uint_ty_range};
32
33#[doc = r" The `unused_comparisons` lint detects comparisons made useless by"]
#[doc = r" limits of the types involved."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" fn foo(x: u8) {"]
#[doc = r" x >= 0;"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" A useless comparison may indicate a mistake, and should be fixed or"]
#[doc = r" removed."]
static UNUSED_COMPARISONS: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "UNUSED_COMPARISONS",
default_level: ::rustc_lint_defs::Warn,
desc: "comparisons made useless by limits of the types involved",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
34 UNUSED_COMPARISONS,
52 Warn,
53 "comparisons made useless by limits of the types involved"
54}
55
56#[doc =
r" The `overflowing_literals` lint detects literals out of range for their type."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" let x: u8 = 1000;"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" It is usually a mistake to use a literal that overflows its type"]
#[doc = r" Change either the literal or its type such that the literal is"]
#[doc = r" within the range of its type."]
static OVERFLOWING_LITERALS: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "OVERFLOWING_LITERALS",
default_level: ::rustc_lint_defs::Deny,
desc: "literal out of range for its type",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
57 OVERFLOWING_LITERALS,
73 Deny,
74 "literal out of range for its type"
75}
76
77#[doc =
r" The `variant_size_differences` lint detects enums with widely varying"]
#[doc = r" variant sizes."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" #![deny(variant_size_differences)]"]
#[doc = r" enum En {"]
#[doc = r" V0(u8),"]
#[doc = r" VBig([u8; 1024]),"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" It can be a mistake to add a variant to an enum that is much larger"]
#[doc =
r" than the other variants, bloating the overall size required for all"]
#[doc = r" variants. This can impact performance and memory usage. This is"]
#[doc = r" triggered if one variant is more than 3 times larger than the"]
#[doc = r" second-largest variant."]
#[doc = r""]
#[doc =
r" Consider placing the large variant's contents on the heap (for example"]
#[doc = r" via [`Box`]) to keep the overall size of the enum itself down."]
#[doc = r""]
#[doc =
r#" This lint is "allow" by default because it can be noisy, and may not be"#]
#[doc = r" an actual problem. Decisions about this should be guided with"]
#[doc = r" profiling and benchmarking."]
#[doc = r""]
#[doc = r" [`Box`]: https://doc.rust-lang.org/std/boxed/index.html"]
static VARIANT_SIZE_DIFFERENCES: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "VARIANT_SIZE_DIFFERENCES",
default_level: ::rustc_lint_defs::Allow,
desc: "detects enums with widely varying variant sizes",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
78 VARIANT_SIZE_DIFFERENCES,
110 Allow,
111 "detects enums with widely varying variant sizes"
112}
113
114#[doc =
r" The `invalid_nan_comparisons` lint checks comparison with `f32::NAN` or `f64::NAN`"]
#[doc = r" as one of the operand."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" let a = 2.3f32;"]
#[doc = r" if a == f32::NAN {}"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" NaN does not compare meaningfully to anything – not"]
#[doc = r" even itself – so those comparisons are always false."]
static INVALID_NAN_COMPARISONS: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "INVALID_NAN_COMPARISONS",
default_level: ::rustc_lint_defs::Warn,
desc: "detects invalid floating point NaN comparisons",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
115 INVALID_NAN_COMPARISONS,
132 Warn,
133 "detects invalid floating point NaN comparisons"
134}
135
136#[doc = r" The `ambiguous_wide_pointer_comparisons` lint checks comparison"]
#[doc = r" of `*const/*mut ?Sized` as the operands."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" # struct A;"]
#[doc = r" # struct B;"]
#[doc = r""]
#[doc = r" # trait T {}"]
#[doc = r" # impl T for A {}"]
#[doc = r" # impl T for B {}"]
#[doc = r""]
#[doc = r" let ab = (A, B);"]
#[doc = r" let a = &ab.0 as *const dyn T;"]
#[doc = r" let b = &ab.1 as *const dyn T;"]
#[doc = r""]
#[doc = r" let _ = a == b;"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" The comparison includes metadata which may not be expected."]
static AMBIGUOUS_WIDE_POINTER_COMPARISONS: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "AMBIGUOUS_WIDE_POINTER_COMPARISONS",
default_level: ::rustc_lint_defs::Warn,
desc: "detects ambiguous wide pointer comparisons",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
137 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
163 Warn,
164 "detects ambiguous wide pointer comparisons"
165}
166
167#[doc =
r" The `unpredictable_function_pointer_comparisons` lint checks comparison"]
#[doc = r" of function pointer as the operands."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" fn a() {}"]
#[doc = r" fn b() {}"]
#[doc = r""]
#[doc = r" let f: fn() = a;"]
#[doc = r" let g: fn() = b;"]
#[doc = r""]
#[doc = r" let _ = f == g;"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" Function pointers comparisons do not produce meaningful result since"]
#[doc =
r" they are never guaranteed to be unique and could vary between different"]
#[doc =
r" code generation units. Furthermore, different functions could have the"]
#[doc = r" same address after being merged together."]
static UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS",
default_level: ::rustc_lint_defs::Warn,
desc: "detects unpredictable function pointer comparisons",
is_externally_loaded: false,
report_in_external_macro: true,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
168 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
192 Warn,
193 "detects unpredictable function pointer comparisons",
194 report_in_external_macro
195}
196
197#[derive(#[automatically_derived]
impl ::core::marker::Copy for TypeLimits { }Copy, #[automatically_derived]
impl ::core::clone::Clone for TypeLimits {
#[inline]
fn clone(&self) -> TypeLimits {
let _: ::core::clone::AssertParamIsClone<Option<NegationInfo>>;
*self
}
}Clone, #[automatically_derived]
impl ::core::default::Default for TypeLimits {
#[inline]
fn default() -> TypeLimits {
TypeLimits {
last_visited_negation: ::core::default::Default::default(),
}
}
}Default)]
198pub(crate) struct TypeLimits {
199 last_visited_negation: Option<NegationInfo>,
200}
201
202#[derive(#[automatically_derived]
impl ::core::marker::Copy for NegationInfo { }Copy, #[automatically_derived]
impl ::core::clone::Clone for NegationInfo {
#[inline]
fn clone(&self) -> NegationInfo {
let _: ::core::clone::AssertParamIsClone<Span>;
let _: ::core::clone::AssertParamIsClone<hir::HirId>;
*self
}
}Clone)]
203struct NegationInfo {
204 negation_span: Span,
206 negated_id: hir::HirId,
208}
209
210impl ::rustc_lint_defs::LintPass for TypeLimits {
fn name(&self) -> &'static str { "TypeLimits" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[UNUSED_COMPARISONS, OVERFLOWING_LITERALS,
INVALID_NAN_COMPARISONS, AMBIGUOUS_WIDE_POINTER_COMPARISONS,
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS]))
}
}
impl TypeLimits {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[UNUSED_COMPARISONS, OVERFLOWING_LITERALS,
INVALID_NAN_COMPARISONS, AMBIGUOUS_WIDE_POINTER_COMPARISONS,
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS]))
}
}impl_lint_pass!(TypeLimits => [
211 UNUSED_COMPARISONS,
212 OVERFLOWING_LITERALS,
213 INVALID_NAN_COMPARISONS,
214 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
215 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS
216]);
217
218impl TypeLimits {
219 pub(crate) fn new() -> TypeLimits {
220 TypeLimits { last_visited_negation: None }
221 }
222}
223
224fn lint_nan<'tcx>(
225 cx: &LateContext<'tcx>,
226 e: &'tcx hir::Expr<'tcx>,
227 binop: hir::BinOpKind,
228 l: &'tcx hir::Expr<'tcx>,
229 r: &'tcx hir::Expr<'tcx>,
230) {
231 fn is_nan(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
232 let expr = expr.peel_blocks().peel_borrows();
233 match expr.kind {
234 ExprKind::Path(qpath) => {
235 let Some(def_id) = cx.typeck_results().qpath_res(&qpath, expr.hir_id).opt_def_id()
236 else {
237 return false;
238 };
239
240 #[allow(non_exhaustive_omitted_patterns)] match cx.tcx.get_diagnostic_name(def_id)
{
Some(sym::f16_nan | sym::f32_nan | sym::f64_nan | sym::f128_nan) => true,
_ => false,
}matches!(
241 cx.tcx.get_diagnostic_name(def_id),
242 Some(sym::f16_nan | sym::f32_nan | sym::f64_nan | sym::f128_nan)
243 )
244 }
245 _ => false,
246 }
247 }
248
249 fn eq_ne(
250 e: &hir::Expr<'_>,
251 l: &hir::Expr<'_>,
252 r: &hir::Expr<'_>,
253 f: impl FnOnce(Span, Span) -> InvalidNanComparisonsSuggestion,
254 ) -> InvalidNanComparisons {
255 let suggestion = if let Some(l_span) = l.span.find_ancestor_inside(e.span)
256 && let Some(r_span) = r.span.find_ancestor_inside(e.span)
257 {
258 f(l_span, r_span)
259 } else {
260 InvalidNanComparisonsSuggestion::Spanless
261 };
262
263 InvalidNanComparisons::EqNe { suggestion }
264 }
265
266 let lint = match binop {
267 hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => {
268 eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
269 nan_plus_binop: l_span.until(r_span),
270 float: r_span.shrink_to_hi(),
271 neg: (binop == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
272 })
273 }
274 hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => {
275 eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
276 nan_plus_binop: l_span.shrink_to_hi().to(r_span),
277 float: l_span.shrink_to_hi(),
278 neg: (binop == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
279 })
280 }
281 hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge
282 if is_nan(cx, l) || is_nan(cx, r) =>
283 {
284 InvalidNanComparisons::LtLeGtGe
285 }
286 _ => return,
287 };
288
289 cx.emit_span_lint(INVALID_NAN_COMPARISONS, e.span, lint);
290}
291
292#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ComparisonOp {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
ComparisonOp::BinOp(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "BinOp",
&__self_0),
ComparisonOp::Other =>
::core::fmt::Formatter::write_str(f, "Other"),
}
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for ComparisonOp {
#[inline]
fn eq(&self, other: &ComparisonOp) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(ComparisonOp::BinOp(__self_0), ComparisonOp::BinOp(__arg1_0))
=> __self_0 == __arg1_0,
_ => true,
}
}
}PartialEq, #[automatically_derived]
impl ::core::marker::Copy for ComparisonOp { }Copy, #[automatically_derived]
impl ::core::clone::Clone for ComparisonOp {
#[inline]
fn clone(&self) -> ComparisonOp {
let _: ::core::clone::AssertParamIsClone<hir::BinOpKind>;
*self
}
}Clone)]
293enum ComparisonOp {
294 BinOp(hir::BinOpKind),
295 Other,
296}
297
298fn lint_wide_pointer<'tcx>(
299 cx: &LateContext<'tcx>,
300 e: &'tcx hir::Expr<'tcx>,
301 cmpop: ComparisonOp,
302 l: &'tcx hir::Expr<'tcx>,
303 r: &'tcx hir::Expr<'tcx>,
304) {
305 let ptr_unsized = |mut ty: Ty<'tcx>| -> Option<(
306 usize,
307 String,
308 bool,
309 )> {
310 let mut refs = 0;
311 while let ty::Ref(_, inner_ty, _) = ty.kind() {
314 ty = *inner_ty;
315 refs += 1;
316 }
317
318 let mut modifiers = String::new();
320 ty = match ty.kind() {
321 ty::RawPtr(ty, _) => *ty,
322 ty::Adt(def, args) if cx.tcx.is_diagnostic_item(sym::NonNull, def.did()) => {
323 modifiers.push_str(".as_ptr()");
324 args.type_at(0)
325 }
326 _ => return None,
327 };
328
329 (!ty.is_sized(cx.tcx, cx.typing_env()))
330 .then(|| (refs, modifiers, #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Dynamic(_, _) => true,
_ => false,
}matches!(ty.kind(), ty::Dynamic(_, _))))
331 };
332
333 let l = l.peel_borrows();
335 let r = r.peel_borrows();
336
337 let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else {
338 return;
339 };
340 let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else {
341 return;
342 };
343
344 let Some((l_ty_refs, l_modifiers, l_inner_ty_is_dyn)) = ptr_unsized(l_ty) else {
345 return;
346 };
347 let Some((r_ty_refs, r_modifiers, r_inner_ty_is_dyn)) = ptr_unsized(r_ty) else {
348 return;
349 };
350
351 let (Some(l_span), Some(r_span)) =
352 (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
353 else {
354 return cx.emit_span_lint(
355 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
356 e.span,
357 AmbiguousWidePointerComparisons::Spanless,
358 );
359 };
360
361 let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
362 let is_eq_ne = #[allow(non_exhaustive_omitted_patterns)] match cmpop {
ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne) => true,
_ => false,
}matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne));
363 let is_dyn_comparison = l_inner_ty_is_dyn && r_inner_ty_is_dyn;
364 let via_method_call = #[allow(non_exhaustive_omitted_patterns)] match &e.kind {
ExprKind::MethodCall(..) | ExprKind::Call(..) => true,
_ => false,
}matches!(&e.kind, ExprKind::MethodCall(..) | ExprKind::Call(..));
365
366 let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
367 let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
368 let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
369
370 let deref_left = &*"*".repeat(l_ty_refs);
371 let deref_right = &*"*".repeat(r_ty_refs);
372
373 let l_modifiers = &*l_modifiers;
374 let r_modifiers = &*r_modifiers;
375
376 cx.emit_span_lint(
377 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
378 e.span,
379 if is_eq_ne {
380 AmbiguousWidePointerComparisons::SpanfulEq {
381 addr_metadata_suggestion: (!is_dyn_comparison).then(|| {
382 AmbiguousWidePointerComparisonsAddrMetadataSuggestion {
383 ne,
384 deref_left,
385 deref_right,
386 l_modifiers,
387 r_modifiers,
388 left,
389 middle,
390 right,
391 }
392 }),
393 addr_suggestion: AmbiguousWidePointerComparisonsAddrSuggestion {
394 ne,
395 deref_left,
396 deref_right,
397 l_modifiers,
398 r_modifiers,
399 left,
400 middle,
401 right,
402 },
403 }
404 } else {
405 AmbiguousWidePointerComparisons::SpanfulCmp {
406 cast_suggestion: AmbiguousWidePointerComparisonsCastSuggestion {
407 deref_left,
408 deref_right,
409 l_modifiers,
410 r_modifiers,
411 paren_left: if l_ty_refs != 0 { ")" } else { "" },
412 paren_right: if r_ty_refs != 0 { ")" } else { "" },
413 left_before: (l_ty_refs != 0).then_some(l_span.shrink_to_lo()),
414 left_after: l_span.shrink_to_hi(),
415 right_before: (r_ty_refs != 0).then_some(r_span.shrink_to_lo()),
416 right_after: r_span.shrink_to_hi(),
417 },
418 expect_suggestion: AmbiguousWidePointerComparisonsExpectSuggestion {
419 paren_left: if via_method_call { "" } else { "(" },
420 paren_right: if via_method_call { "" } else { ")" },
421 before: e.span.shrink_to_lo(),
422 after: e.span.shrink_to_hi(),
423 },
424 }
425 },
426 );
427}
428
429fn lint_fn_pointer<'tcx>(
430 cx: &LateContext<'tcx>,
431 e: &'tcx hir::Expr<'tcx>,
432 cmpop: ComparisonOp,
433 l: &'tcx hir::Expr<'tcx>,
434 r: &'tcx hir::Expr<'tcx>,
435) {
436 let peel_refs = |mut ty: Ty<'tcx>| -> (Ty<'tcx>, usize) {
437 let mut refs = 0;
438
439 while let ty::Ref(_, inner_ty, _) = ty.kind() {
440 ty = *inner_ty;
441 refs += 1;
442 }
443
444 (ty, refs)
445 };
446
447 let l = l.peel_borrows();
449 let r = r.peel_borrows();
450
451 let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else { return };
452 let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else { return };
453
454 let (l_ty, l_ty_refs) = peel_refs(l_ty);
457 let (r_ty, r_ty_refs) = peel_refs(r_ty);
458
459 if l_ty.is_fn() && r_ty.is_fn() {
460 } else if let ty::Adt(l_def, l_args) = l_ty.kind()
462 && let ty::Adt(r_def, r_args) = r_ty.kind()
463 && cx.tcx.is_lang_item(l_def.did(), LangItem::Option)
464 && cx.tcx.is_lang_item(r_def.did(), LangItem::Option)
465 && let Some(l_some_arg) = l_args.get(0)
466 && let Some(r_some_arg) = r_args.get(0)
467 && l_some_arg.expect_ty().is_fn()
468 && r_some_arg.expect_ty().is_fn()
469 {
470 return cx.emit_span_lint(
472 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
473 e.span,
474 UnpredictableFunctionPointerComparisons::Warn,
475 );
476 } else {
477 return;
479 }
480
481 let is_eq_ne = #[allow(non_exhaustive_omitted_patterns)] match cmpop {
ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne) => true,
_ => false,
}matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne));
484
485 if !is_eq_ne {
486 return cx.emit_span_lint(
488 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
489 e.span,
490 UnpredictableFunctionPointerComparisons::Warn,
491 );
492 }
493
494 let (Some(l_span), Some(r_span)) =
495 (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
496 else {
497 return cx.emit_span_lint(
499 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
500 e.span,
501 UnpredictableFunctionPointerComparisons::Warn,
502 );
503 };
504
505 let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
506
507 let deref_left = &*"*".repeat(l_ty_refs);
509 let deref_right = &*"*".repeat(r_ty_refs);
510
511 let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
512 let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
513 let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
514
515 let sugg =
516 if !r_ty.is_fn_ptr() {
519 let fn_sig = r_ty.fn_sig(cx.tcx);
520
521 UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEqWithCast {
522 ne,
523 fn_sig,
524 deref_left,
525 deref_right,
526 left,
527 middle,
528 right,
529 }
530 } else {
531 UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEq {
532 ne,
533 deref_left,
534 deref_right,
535 left,
536 middle,
537 right,
538 }
539 };
540
541 cx.emit_span_lint(
542 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
543 e.span,
544 UnpredictableFunctionPointerComparisons::Suggestion { sugg },
545 );
546}
547
548impl<'tcx> LateLintPass<'tcx> for TypeLimits {
549 fn check_lit(
550 &mut self,
551 cx: &LateContext<'tcx>,
552 hir_id: HirId,
553 lit: hir::Lit,
554 is_negated_pat: bool,
555 ) {
556 let surrounding_negation = if is_negated_pat {
557 Some(lit.span)
560 } else if let Some(negation_info) = self.last_visited_negation
561 && negation_info.negated_id == hir_id
562 {
563 Some(negation_info.negation_span)
564 } else {
565 None
566 };
567 lint_literal(cx, hir_id, lit.span, &lit, surrounding_negation);
568 }
569
570 fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
571 match e.kind {
572 hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
573 self.last_visited_negation =
574 Some(NegationInfo { negation_span: e.span, negated_id: expr.hir_id });
575 }
576 hir::ExprKind::Binary(binop, ref l, ref r) => {
577 if is_comparison(binop.node) {
578 if !check_limits(cx, binop.node, l, r) {
579 cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
580 } else {
581 lint_nan(cx, e, binop.node, l, r);
582 let cmpop = ComparisonOp::BinOp(binop.node);
583 lint_wide_pointer(cx, e, cmpop, l, r);
584 lint_fn_pointer(cx, e, cmpop, l, r);
585 }
586 }
587 }
588 hir::ExprKind::Call(path, [l, r])
589 if let ExprKind::Path(ref qpath) = path.kind
590 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
591 && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
592 && let Some(cmpop) = diag_item_cmpop(diag_item) =>
593 {
594 lint_wide_pointer(cx, e, cmpop, l, r);
595 lint_fn_pointer(cx, e, cmpop, l, r);
596 }
597 hir::ExprKind::MethodCall(_, l, [r], _)
598 if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
599 && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
600 && let Some(cmpop) = diag_item_cmpop(diag_item) =>
601 {
602 lint_wide_pointer(cx, e, cmpop, l, r);
603 lint_fn_pointer(cx, e, cmpop, l, r);
604 }
605 _ => {}
606 };
607
608 fn is_valid<T: PartialOrd>(binop: hir::BinOpKind, v: T, min: T, max: T) -> bool {
609 match binop {
610 hir::BinOpKind::Lt => v > min && v <= max,
611 hir::BinOpKind::Le => v >= min && v < max,
612 hir::BinOpKind::Gt => v >= min && v < max,
613 hir::BinOpKind::Ge => v > min && v <= max,
614 hir::BinOpKind::Eq | hir::BinOpKind::Ne => v >= min && v <= max,
615 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
616 }
617 }
618
619 fn rev_binop(binop: hir::BinOpKind) -> hir::BinOpKind {
620 match binop {
621 hir::BinOpKind::Lt => hir::BinOpKind::Gt,
622 hir::BinOpKind::Le => hir::BinOpKind::Ge,
623 hir::BinOpKind::Gt => hir::BinOpKind::Lt,
624 hir::BinOpKind::Ge => hir::BinOpKind::Le,
625 _ => binop,
626 }
627 }
628
629 fn check_limits(
630 cx: &LateContext<'_>,
631 binop: hir::BinOpKind,
632 l: &hir::Expr<'_>,
633 r: &hir::Expr<'_>,
634 ) -> bool {
635 let (lit, expr, swap) = match (&l.kind, &r.kind) {
636 (&hir::ExprKind::Lit(_), _) => (l, r, true),
637 (_, &hir::ExprKind::Lit(_)) => (r, l, false),
638 _ => return true,
639 };
640 let norm_binop = if swap { rev_binop(binop) } else { binop };
643 match *cx.typeck_results().node_type(expr.hir_id).kind() {
644 ty::Int(int_ty) => {
645 let (min, max) = int_ty_range(int_ty);
646 let lit_val: i128 = match lit.kind {
647 hir::ExprKind::Lit(li) => match li.node {
648 ast::LitKind::Int(
649 v,
650 ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed,
651 ) => v.get() as i128,
652 _ => return true,
653 },
654 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
655 };
656 is_valid(norm_binop, lit_val, min, max)
657 }
658 ty::Uint(uint_ty) => {
659 let (min, max): (u128, u128) = uint_ty_range(uint_ty);
660 let lit_val: u128 = match lit.kind {
661 hir::ExprKind::Lit(li) => match li.node {
662 ast::LitKind::Int(v, _) => v.get(),
663 _ => return true,
664 },
665 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
666 };
667 is_valid(norm_binop, lit_val, min, max)
668 }
669 _ => true,
670 }
671 }
672
673 fn is_comparison(binop: hir::BinOpKind) -> bool {
674 #[allow(non_exhaustive_omitted_patterns)] match binop {
hir::BinOpKind::Eq | hir::BinOpKind::Lt | hir::BinOpKind::Le |
hir::BinOpKind::Ne | hir::BinOpKind::Ge | hir::BinOpKind::Gt => true,
_ => false,
}matches!(
675 binop,
676 hir::BinOpKind::Eq
677 | hir::BinOpKind::Lt
678 | hir::BinOpKind::Le
679 | hir::BinOpKind::Ne
680 | hir::BinOpKind::Ge
681 | hir::BinOpKind::Gt
682 )
683 }
684
685 fn diag_item_cmpop(diag_item: Symbol) -> Option<ComparisonOp> {
686 Some(match diag_item {
687 sym::cmp_ord_max => ComparisonOp::Other,
688 sym::cmp_ord_min => ComparisonOp::Other,
689 sym::ord_cmp_method => ComparisonOp::Other,
690 sym::cmp_partialeq_eq => ComparisonOp::BinOp(hir::BinOpKind::Eq),
691 sym::cmp_partialeq_ne => ComparisonOp::BinOp(hir::BinOpKind::Ne),
692 sym::cmp_partialord_cmp => ComparisonOp::Other,
693 sym::cmp_partialord_ge => ComparisonOp::BinOp(hir::BinOpKind::Ge),
694 sym::cmp_partialord_gt => ComparisonOp::BinOp(hir::BinOpKind::Gt),
695 sym::cmp_partialord_le => ComparisonOp::BinOp(hir::BinOpKind::Le),
696 sym::cmp_partialord_lt => ComparisonOp::BinOp(hir::BinOpKind::Lt),
697 _ => return None,
698 })
699 }
700 }
701}
702
703pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
704 tcx: TyCtxt<'tcx>,
705 def: ty::AdtDef<'tcx>,
706) -> bool {
707 {
{
'done:
{
for i in
::rustc_hir::attrs::HasAttrs::get_attrs(def.did(), &tcx) {
#[allow(unused_imports)]
use ::rustc_hir::attrs::AttributeKind::*;
let i: &::rustc_hir::Attribute = i;
match i {
::rustc_hir::Attribute::Parsed(RustcNonnullOptimizationGuaranteed)
=> {
break 'done Some(());
}
::rustc_hir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}
}.is_some()find_attr!(tcx, def.did(), RustcNonnullOptimizationGuaranteed)
708}
709
710pub(crate) fn transparent_newtype_field<'a, 'tcx>(
713 tcx: TyCtxt<'tcx>,
714 variant: &'a ty::VariantDef,
715) -> Option<&'a ty::FieldDef> {
716 let typing_env = ty::TypingEnv::non_body_analysis(tcx, variant.def_id);
717 variant.fields.iter().find(|field| {
718 let field_ty = tcx.type_of(field.did).instantiate_identity().skip_norm_wip();
719 let is_1zst =
720 tcx.layout_of(typing_env.as_query_input(field_ty)).is_ok_and(|layout| layout.is_1zst());
721 !is_1zst
722 })
723}
724
725fn ty_is_known_nonnull<'tcx>(
727 tcx: TyCtxt<'tcx>,
728 typing_env: ty::TypingEnv<'tcx>,
729 ty: Ty<'tcx>,
730) -> bool {
731 let ty = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty);
732
733 match ty.kind() {
734 ty::FnPtr(..) => true,
735 ty::Ref(..) => true,
736 ty::Adt(def, _) if def.is_box() => true,
737 ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => {
738 let marked_non_null = nonnull_optimization_guaranteed(tcx, *def);
739
740 if marked_non_null {
741 return true;
742 }
743
744 if def.is_unsafe_cell() || def.is_unsafe_pinned() {
746 return false;
747 }
748
749 def.variants().iter().filter_map(|variant| transparent_newtype_field(tcx, variant)).any(
750 |field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args).skip_norm_wip()),
751 )
752 }
753 ty::Pat(base, pat) => {
754 ty_is_known_nonnull(tcx, typing_env, *base)
755 || pat_ty_is_known_nonnull(tcx, typing_env, *pat)
756 }
757 _ => false,
758 }
759}
760
761fn pat_ty_is_known_nonnull<'tcx>(
762 tcx: TyCtxt<'tcx>,
763 typing_env: ty::TypingEnv<'tcx>,
764 pat: ty::Pattern<'tcx>,
765) -> bool {
766 try {
767 match *pat {
768 ty::PatternKind::Range { start, end } => {
769 let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?;
770 let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?;
771
772 start > 0 && end >= start
775 }
776 ty::PatternKind::NotNull => true,
777 ty::PatternKind::Or(patterns) => {
778 patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat))
779 }
780 }
781 }
782 .unwrap_or_default()
783}
784
785fn get_nullable_type<'tcx>(
788 tcx: TyCtxt<'tcx>,
789 typing_env: ty::TypingEnv<'tcx>,
790 ty: Ty<'tcx>,
791) -> Option<Ty<'tcx>> {
792 let ty = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty);
793
794 Some(match *ty.kind() {
795 ty::Adt(field_def, field_args) => {
796 let inner_field_ty = {
797 let mut first_non_zst_ty =
798 field_def.variants().iter().filter_map(|v| transparent_newtype_field(tcx, v));
799 if true {
{
match (&first_non_zst_ty.clone().count(), &1) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val,
::core::option::Option::Some(format_args!("Wrong number of fields for transparent type")));
}
}
}
};
};debug_assert_eq!(
800 first_non_zst_ty.clone().count(),
801 1,
802 "Wrong number of fields for transparent type"
803 );
804 first_non_zst_ty
805 .next_back()
806 .expect("No non-zst fields in transparent type.")
807 .ty(tcx, field_args)
808 .skip_norm_wip()
809 };
810 return get_nullable_type(tcx, typing_env, inner_field_ty);
811 }
812 ty::Pat(base, ..) => return get_nullable_type(tcx, typing_env, base),
813 ty::Int(_) | ty::Uint(_) | ty::Char | ty::RawPtr(..) => ty,
814 ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl),
817 ty::FnPtr(..) => ty,
820 ref unhandled => {
823 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/types.rs:823",
"rustc_lint::types", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/types.rs"),
::tracing_core::__macro_support::Option::Some(823u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::types"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("get_nullable_type: Unhandled scalar kind: {0:?} while checking {1:?}",
unhandled, ty) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(
824 "get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}",
825 unhandled, ty
826 );
827 return None;
828 }
829 })
830}
831
832fn is_niche_optimization_candidate<'tcx>(
837 tcx: TyCtxt<'tcx>,
838 typing_env: ty::TypingEnv<'tcx>,
839 ty: Ty<'tcx>,
840) -> bool {
841 if tcx.layout_of(typing_env.as_query_input(ty)).is_ok_and(|layout| !layout.is_1zst()) {
842 return false;
843 }
844
845 match ty.kind() {
846 ty::Adt(ty_def, _) => {
847 let non_exhaustive = ty_def.is_variant_list_non_exhaustive();
848 let empty = (ty_def.is_struct() && ty_def.non_enum_variant().fields.is_empty())
849 || (ty_def.is_enum() && ty_def.variants().is_empty());
850
851 !non_exhaustive && empty
852 }
853 ty::Tuple(tys) => tys.is_empty(),
854 _ => false,
855 }
856}
857
858pub(crate) fn repr_nullable_ptr<'tcx>(
863 tcx: TyCtxt<'tcx>,
864 typing_env: ty::TypingEnv<'tcx>,
865 ty: Ty<'tcx>,
866) -> Option<Ty<'tcx>> {
867 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/types.rs:867",
"rustc_lint::types", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/types.rs"),
::tracing_core::__macro_support::Option::Some(867u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::types"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("is_repr_nullable_ptr(tcx, ty = {0:?})",
ty) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty);
868 match ty.kind() {
869 ty::Adt(ty_def, args) => {
870 let field_ty = match &ty_def.variants().raw[..] {
871 [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
872 ([], [field]) | ([field], []) => field.ty(tcx, args).skip_norm_wip(),
873 ([field1], [field2]) => {
874 let ty1 = field1.ty(tcx, args).skip_norm_wip();
875 let ty2 = field2.ty(tcx, args).skip_norm_wip();
876
877 if is_niche_optimization_candidate(tcx, typing_env, ty1) {
878 ty2
879 } else if is_niche_optimization_candidate(tcx, typing_env, ty2) {
880 ty1
881 } else {
882 return None;
883 }
884 }
885 _ => return None,
886 },
887 _ => return None,
888 };
889
890 if !ty_is_known_nonnull(tcx, typing_env, field_ty) {
891 return None;
892 }
893
894 let compute_size_skeleton =
898 |t| SizeSkeleton::compute(t, tcx, typing_env, DUMMY_SP).ok();
899 if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) {
900 ::rustc_middle::util::bug::bug_fmt(format_args!("improper_ctypes: Option nonnull optimization not applied?"));bug!("improper_ctypes: Option nonnull optimization not applied?");
901 }
902
903 let field_ty_layout = tcx.layout_of(typing_env.as_query_input(field_ty));
905 if field_ty_layout.is_err() && !field_ty.has_non_region_param() {
906 ::rustc_middle::util::bug::bug_fmt(format_args!("should be able to compute the layout of non-polymorphic type"));bug!("should be able to compute the layout of non-polymorphic type");
907 }
908
909 let field_ty_abi = &field_ty_layout.ok()?.backend_repr;
910 if let BackendRepr::Scalar(field_ty_scalar) = field_ty_abi {
911 match field_ty_scalar.valid_range(&tcx) {
912 WrappingRange { start: 0, end }
913 if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 =>
914 {
915 return Some(get_nullable_type(tcx, typing_env, field_ty).expect(
916 "known non-null scalar type should have a nullable representation",
917 ));
918 }
919 WrappingRange { start: 1, .. } => {
920 return Some(get_nullable_type(tcx, typing_env, field_ty).expect(
921 "known non-null scalar type should have a nullable representation",
922 ));
923 }
924 WrappingRange { start, end } => {
925 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Unhandled start and end range: ({0}, {1})", start,
end)));
}unreachable!("Unhandled start and end range: ({}, {})", start, end)
926 }
927 };
928 }
929 None
930 }
931 ty::Pat(base, pat) => get_nullable_type_from_pat(tcx, typing_env, *base, *pat),
932 _ => None,
933 }
934}
935
936fn get_nullable_type_from_pat<'tcx>(
937 tcx: TyCtxt<'tcx>,
938 typing_env: ty::TypingEnv<'tcx>,
939 base: Ty<'tcx>,
940 pat: ty::Pattern<'tcx>,
941) -> Option<Ty<'tcx>> {
942 match *pat {
943 ty::PatternKind::NotNull | ty::PatternKind::Range { .. } => {
944 get_nullable_type(tcx, typing_env, base)
945 }
946 ty::PatternKind::Or(patterns) => {
947 let first = get_nullable_type_from_pat(tcx, typing_env, base, patterns[0])?;
948 for &pat in &patterns[1..] {
949 {
match (&first, &get_nullable_type_from_pat(tcx, typing_env, base, pat)?) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};assert_eq!(first, get_nullable_type_from_pat(tcx, typing_env, base, pat)?);
950 }
951 Some(first)
952 }
953 }
954}
955
956pub struct VariantSizeDifferences;
#[automatically_derived]
impl ::core::marker::Copy for VariantSizeDifferences { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for VariantSizeDifferences { }
#[automatically_derived]
impl ::core::clone::Clone for VariantSizeDifferences {
#[inline]
fn clone(&self) -> VariantSizeDifferences { *self }
}
impl ::rustc_lint_defs::LintPass for VariantSizeDifferences {
fn name(&self) -> &'static str { "VariantSizeDifferences" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[VARIANT_SIZE_DIFFERENCES]))
}
}
impl VariantSizeDifferences {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[VARIANT_SIZE_DIFFERENCES]))
}
}declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]);
957
958impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
959 fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
960 if let hir::ItemKind::Enum(_, _, ref enum_definition) = it.kind {
961 let t = cx.tcx.type_of(it.owner_id).instantiate_identity().skip_norm_wip();
962 let ty = cx.tcx.erase_and_anonymize_regions(t);
963 let Ok(layout) = cx.layout_of(ty) else { return };
964 let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, variants, .. } =
965 &layout.variants
966 else {
967 return;
968 };
969
970 let tag_size = tag.size(&cx.tcx).bytes();
971
972 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/types.rs:972",
"rustc_lint::types", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/types.rs"),
::tracing_core::__macro_support::Option::Some(972u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::types"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("enum `{0}` is {1} bytes large with layout:\n{2:#?}",
t, layout.size.bytes(), layout) as
&dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(
973 "enum `{}` is {} bytes large with layout:\n{:#?}",
974 t,
975 layout.size.bytes(),
976 layout
977 );
978
979 let (largest, slargest, largest_index) = iter::zip(enum_definition.variants, variants)
980 .map(|(variant, variant_layout)| {
981 let bytes = variant_layout.size.bytes().saturating_sub(tag_size);
983
984 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/types.rs:984",
"rustc_lint::types", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/types.rs"),
::tracing_core::__macro_support::Option::Some(984u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::types"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("- variant `{0}` is {1} bytes large",
variant.ident, bytes) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("- variant `{}` is {} bytes large", variant.ident, bytes);
985 bytes
986 })
987 .enumerate()
988 .fold((0, 0, 0), |(l, s, li), (idx, size)| {
989 if size > l {
990 (size, l, idx)
991 } else if size > s {
992 (l, size, li)
993 } else {
994 (l, s, li)
995 }
996 });
997
998 if largest > slargest * 3 && slargest > 0 {
1001 cx.emit_span_lint(
1002 VARIANT_SIZE_DIFFERENCES,
1003 enum_definition.variants[largest_index].span,
1004 VariantSizeDifferencesDiag { largest },
1005 );
1006 }
1007 }
1008 }
1009}
1010
1011#[doc = r" The `invalid_atomic_ordering` lint detects passing an `Ordering`"]
#[doc = r" to an atomic operation that does not support that ordering."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" # use core::sync::atomic::{AtomicU8, Ordering};"]
#[doc = r" let atom = AtomicU8::new(0);"]
#[doc = r" let value = atom.load(Ordering::Release);"]
#[doc = r" # let _ = value;"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" Some atomic operations are only supported for a subset of the"]
#[doc =
r" `atomic::Ordering` variants. Passing an unsupported variant will cause"]
#[doc =
r" an unconditional panic at runtime, which is detected by this lint."]
#[doc = r""]
#[doc =
r" This lint will trigger in the following cases: (where `AtomicType` is an"]
#[doc = r" atomic type from `core::sync::atomic`, such as `AtomicBool`,"]
#[doc = r" `AtomicPtr`, `AtomicUsize`, or any of the other integer atomics)."]
#[doc = r""]
#[doc = r" - Passing `Ordering::Acquire` or `Ordering::AcqRel` to"]
#[doc = r" `AtomicType::store`."]
#[doc = r""]
#[doc = r" - Passing `Ordering::Release` or `Ordering::AcqRel` to"]
#[doc = r" `AtomicType::load`."]
#[doc = r""]
#[doc = r" - Passing `Ordering::Relaxed` to `core::sync::atomic::fence` or"]
#[doc = r" `core::sync::atomic::compiler_fence`."]
#[doc = r""]
#[doc =
r" - Passing `Ordering::Release` or `Ordering::AcqRel` as the failure"]
#[doc = r" ordering for any of `AtomicType::compare_exchange`,"]
#[doc = r" `AtomicType::compare_exchange_weak`, `AtomicType::update`, or"]
#[doc = r" `AtomicType::try_update`."]
static INVALID_ATOMIC_ORDERING: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "INVALID_ATOMIC_ORDERING",
default_level: ::rustc_lint_defs::Deny,
desc: "usage of invalid atomic ordering in atomic operations and memory fences",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
1012 INVALID_ATOMIC_ORDERING,
1050 Deny,
1051 "usage of invalid atomic ordering in atomic operations and memory fences"
1052}
1053
1054pub struct InvalidAtomicOrdering;
#[automatically_derived]
impl ::core::marker::Copy for InvalidAtomicOrdering { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for InvalidAtomicOrdering { }
#[automatically_derived]
impl ::core::clone::Clone for InvalidAtomicOrdering {
#[inline]
fn clone(&self) -> InvalidAtomicOrdering { *self }
}
impl ::rustc_lint_defs::LintPass for InvalidAtomicOrdering {
fn name(&self) -> &'static str { "InvalidAtomicOrdering" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[INVALID_ATOMIC_ORDERING]))
}
}
impl InvalidAtomicOrdering {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[INVALID_ATOMIC_ORDERING]))
}
}declare_lint_pass!(InvalidAtomicOrdering => [INVALID_ATOMIC_ORDERING]);
1055
1056impl InvalidAtomicOrdering {
1057 fn inherent_atomic_method_call<'hir>(
1058 cx: &LateContext<'_>,
1059 expr: &Expr<'hir>,
1060 recognized_names: &[Symbol], ) -> Option<(Symbol, &'hir [Expr<'hir>])> {
1062 if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind
1063 && recognized_names.contains(&method_path.ident.name)
1064 && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
1065 && let Some(impl_did) = cx.tcx.inherent_impl_of_assoc(m_def_id)
1067 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().skip_norm_wip().ty_adt_def()
1068 && cx.tcx.is_diagnostic_item(sym::Atomic, adt.did())
1069 {
1070 return Some((method_path.ident.name, args));
1071 }
1072 None
1073 }
1074
1075 fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol> {
1076 let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None };
1077 let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?;
1078 let tcx = cx.tcx;
1079 let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering);
1080 let name = tcx.item_name(did);
1081 let parent = tcx.parent(did);
1082 [sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find(
1083 |&ordering| {
1084 name == ordering
1085 && (Some(parent) == atomic_ordering
1086 || tcx.opt_parent(parent) == atomic_ordering)
1088 },
1089 )
1090 }
1091
1092 fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
1093 if let Some((method, args)) =
1094 Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
1095 && let Some((ordering_arg, invalid_ordering)) = match method {
1096 sym::load => Some((&args[0], sym::Release)),
1097 sym::store => Some((&args[1], sym::Acquire)),
1098 _ => None,
1099 }
1100 && let Some(ordering) = Self::match_ordering(cx, ordering_arg)
1101 && (ordering == invalid_ordering || ordering == sym::AcqRel)
1102 {
1103 if method == sym::load {
1104 cx.emit_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad);
1105 } else {
1106 cx.emit_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore);
1107 };
1108 }
1109 }
1110
1111 fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
1112 if let ExprKind::Call(func, args) = expr.kind
1113 && let ExprKind::Path(ref func_qpath) = func.kind
1114 && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
1115 && #[allow(non_exhaustive_omitted_patterns)] match cx.tcx.get_diagnostic_name(def_id)
{
Some(sym::fence | sym::compiler_fence) => true,
_ => false,
}matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
1116 && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
1117 {
1118 cx.emit_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence);
1119 }
1120 }
1121
1122 fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
1123 let Some((method, args)) = Self::inherent_atomic_method_call(
1124 cx,
1125 expr,
1126 &[
1127 sym::update,
1128 sym::try_update,
1129 sym::fetch_update,
1130 sym::compare_exchange,
1131 sym::compare_exchange_weak,
1132 ],
1133 ) else {
1134 return;
1135 };
1136
1137 let fail_order_arg = match method {
1138 sym::update | sym::try_update | sym::fetch_update => &args[1],
1139 sym::compare_exchange | sym::compare_exchange_weak => &args[3],
1140 _ => return,
1141 };
1142
1143 let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
1144
1145 if #[allow(non_exhaustive_omitted_patterns)] match fail_ordering {
sym::Release | sym::AcqRel => true,
_ => false,
}matches!(fail_ordering, sym::Release | sym::AcqRel) {
1146 cx.emit_span_lint(
1147 INVALID_ATOMIC_ORDERING,
1148 fail_order_arg.span,
1149 InvalidAtomicOrderingDiag { method, fail_order_arg_span: fail_order_arg.span },
1150 );
1151 }
1152 }
1153}
1154
1155impl<'tcx> LateLintPass<'tcx> for InvalidAtomicOrdering {
1156 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1157 Self::check_atomic_load_store(cx, expr);
1158 Self::check_memory_fence(cx, expr);
1159 Self::check_atomic_compare_exchange(cx, expr);
1160 }
1161}