1use std::iter;
2
3use rustc_abi::{BackendRepr, TagEncoding, Variants, WrappingRange};
4use rustc_ast as ast;
5use rustc_ast::util::parser::ExprPrecedence;
6use rustc_attr_ir::find_attr;
7use rustc_attr_ir::lang_items::LangItem;
8use rustc_hir as hir;
9use rustc_hir::{Expr, ExprKind, HirId};
10use rustc_lint_defs::{declare_lint, declare_lint_pass, impl_lint_pass};
11use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton};
12use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Unnormalized};
13use rustc_span::{DUMMY_SP, Span, Symbol, bug, sym};
14use tracing::debug;
15
16mod improper_ctypes; pub(crate) use improper_ctypes::ImproperCTypesLint;
18
19use crate::diagnostics::{
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::utils::std_or_core;
29use crate::{LateContext, LateLintPass, LintContext};
30
31mod literal;
32use literal::{int_ty_range, lint_literal, uint_ty_range};
33
34#[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",
..
};declare_lint! {
35 UNUSED_COMPARISONS,
53 Warn,
54 "comparisons made useless by limits of the types involved"
55}
56
57#[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",
..
};declare_lint! {
58 OVERFLOWING_LITERALS,
74 Deny,
75 "literal out of range for its type"
76}
77
78#[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",
..
};declare_lint! {
79 VARIANT_SIZE_DIFFERENCES,
111 Allow,
112 "detects enums with widely varying variant sizes"
113}
114
115#[doc =
r" The `invalid_nan_comparisons` lint checks comparisons with the `NAN` constants"]
#[doc = r" of `f16`, `f32`, `f64`, and `f128`."]
#[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 compares unequal to every floating-point value, including itself."]
#[doc = r" Use the `is_nan` method to check whether a value is NaN."]
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",
..
};declare_lint! {
116 INVALID_NAN_COMPARISONS,
133 Warn,
134 "detects invalid floating point NaN comparisons"
135}
136
137#[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",
..
};declare_lint! {
138 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
164 Warn,
165 "detects ambiguous wide pointer comparisons"
166}
167
168#[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",
report_in_external_macro: true,
..
};declare_lint! {
169 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
193 Warn,
194 "detects unpredictable function pointer comparisons",
195 report_in_external_macro
196}
197
198#[derive(#[automatically_derived]
impl ::core::marker::Copy for TypeLimits { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for TypeLimits { }
#[automatically_derived]
impl ::core::clone::Clone for TypeLimits {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<Option<NegationInfo>>;
*self
}
}Clone, #[automatically_derived]
impl ::core::default::Default for TypeLimits {
#[inline]
fn default() -> Self {
TypeLimits {
last_visited_negation: ::core::default::Default::default(),
}
}
}Default)]
199pub(crate) struct TypeLimits {
200 last_visited_negation: Option<NegationInfo>,
201}
202
203#[derive(#[automatically_derived]
impl ::core::marker::Copy for NegationInfo { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for NegationInfo { }
#[automatically_derived]
impl ::core::clone::Clone for NegationInfo {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<Span>;
let _: ::core::clone::AssertParamIsClone<hir::HirId>;
*self
}
}Clone)]
204struct NegationInfo {
205 negation_span: Span,
207 negated_id: hir::HirId,
209}
210
211impl ::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 => [
212 UNUSED_COMPARISONS,
213 OVERFLOWING_LITERALS,
214 INVALID_NAN_COMPARISONS,
215 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
216 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS
217]);
218
219impl TypeLimits {
220 pub(crate) fn new() -> TypeLimits {
221 TypeLimits { last_visited_negation: None }
222 }
223}
224
225fn lint_nan<'tcx>(
226 cx: &LateContext<'tcx>,
227 e: &'tcx hir::Expr<'tcx>,
228 binop: hir::BinOpKind,
229 l: &'tcx hir::Expr<'tcx>,
230 r: &'tcx hir::Expr<'tcx>,
231) {
232 fn nan_ty(cx: &LateContext<'_>, mut expr: &hir::Expr<'_>) -> Option<ty::FloatTy> {
233 loop {
234 expr = expr.peel_blocks();
235 match expr.kind {
236 ExprKind::AddrOf(.., inner) => expr = inner,
237 _ => break,
238 }
239 }
240 let ExprKind::Path(qpath) = expr.kind else { return None };
241 let def_id = cx.typeck_results().qpath_res(&qpath, expr.hir_id).opt_def_id()?;
242 Some(match cx.tcx.get_diagnostic_name(def_id)? {
243 sym::f16_nan => ty::FloatTy::F16,
244 sym::f32_nan => ty::FloatTy::F32,
245 sym::f64_nan => ty::FloatTy::F64,
246 sym::f128_nan => ty::FloatTy::F128,
247 _ => return None,
248 })
249 }
250
251 fn nan_method_path(
252 cx: &LateContext<'_>,
253 mut nan: &hir::Expr<'_>,
254 float_ty: ty::FloatTy,
255 cmp_span: Span,
256 ) -> Option<String> {
257 loop {
258 match nan.kind {
259 ExprKind::AddrOf(.., inner) => nan = inner,
260 ExprKind::Block(block, _) if block.stmts.is_empty() => nan = block.expr?,
261 _ => break,
262 }
263 }
264 let ExprKind::Path(hir::QPath::TypeRelative(ty, _)) = nan.kind else {
265 return None;
266 };
267 if !ty.span.from_expansion() && cmp_span.contains(ty.span) {
268 let ty = cx.sess().source_map().span_to_snippet(ty.span).ok()?;
269 Some(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("<{0}>::is_nan", ty))
})format!("<{ty}>::is_nan"))
270 } else if #[allow(non_exhaustive_omitted_patterns)] match float_ty {
ty::FloatTy::F32 | ty::FloatTy::F64 => true,
_ => false,
}matches!(float_ty, ty::FloatTy::F32 | ty::FloatTy::F64) {
271 let krate = std_or_core(cx)?;
272 Some(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("::{1}::primitive::{0}::is_nan",
float_ty.name_str(), krate))
})format!("::{krate}::primitive::{}::is_nan", float_ty.name_str()))
273 } else {
274 None
275 }
276 }
277
278 fn eq_ne_suggestion<'a>(
279 cx: &LateContext<'a>,
280 cmp: &'a hir::Expr<'a>,
281 value: &'a hir::Expr<'a>,
282 nan: &'a hir::Expr<'a>,
283 float_ty: ty::FloatTy,
284 is_ne: bool,
285 ) -> InvalidNanComparisonsSuggestion {
286 if cmp.span.from_expansion() {
287 return InvalidNanComparisonsSuggestion::Spanless;
288 }
289 let (Some(value_span), Some(nan_span)) = (
290 value.span.find_ancestor_inside_same_ctxt(cmp.span),
291 nan.span.find_ancestor_inside_same_ctxt(cmp.span),
292 ) else {
293 return InvalidNanComparisonsSuggestion::Spanless;
294 };
295 if value_span.overlaps(nan_span) {
296 return InvalidNanComparisonsSuggestion::Spanless;
297 }
298 let remove_nan = if nan_span.lo() < value_span.lo() {
299 nan_span.until(value_span)
300 } else {
301 value_span.shrink_to_hi().to(nan_span)
302 };
303
304 let Some(mut ty) = cx.typeck_results().expr_ty_opt(value) else {
305 return InvalidNanComparisonsSuggestion::Spanless;
306 };
307 let mut deref_count = 0;
308 while let ty::Ref(_, inner, _) = *ty.kind() {
309 deref_count += 1;
310 ty = inner;
311 }
312 let neg = if is_ne { "!" } else { "" };
313 let Some(method_path) = nan_method_path(cx, nan, float_ty, cmp.span) else {
314 return InvalidNanComparisonsSuggestion::Spanless;
315 };
316 let (open, suffix) = if deref_count != 0 && cx.precedence(value) < ExprPrecedence::Prefix {
317 ("(", "))")
318 } else {
319 ("", ")")
320 };
321 InvalidNanComparisonsSuggestion::Ufcs {
322 prefix: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{1}{2}({0}{3}",
"*".repeat(deref_count), neg, method_path, open))
})format!("{neg}{method_path}({}{open}", "*".repeat(deref_count)),
323 prefix_span: value_span.shrink_to_lo(),
324 suffix,
325 close_span: value_span.shrink_to_hi(),
326 remove_nan,
327 }
328 }
329
330 let Some((value, nan, float_ty)) = nan_ty(cx, l)
331 .map(|float_ty| (r, l, float_ty))
332 .or_else(|| nan_ty(cx, r).map(|float_ty| (l, r, float_ty)))
333 else {
334 return;
335 };
336 let typeck_results = cx.typeck_results();
337 if ![l, r].into_iter().all(|operand| {
339 typeck_results
340 .expr_ty_opt(operand)
341 .is_some_and(|ty| #[allow(non_exhaustive_omitted_patterns)] match *ty.peel_refs().kind() {
ty::Float(fty) if fty == float_ty => true,
_ => false,
}matches!(*ty.peel_refs().kind(), ty::Float(fty) if fty == float_ty))
342 }) {
343 return;
344 }
345 let lint = match binop {
346 hir::BinOpKind::Eq | hir::BinOpKind::Ne => InvalidNanComparisons::EqNe {
347 suggestion: eq_ne_suggestion(cx, e, value, nan, float_ty, binop == hir::BinOpKind::Ne),
348 },
349 hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge => {
350 InvalidNanComparisons::LtLeGtGe
351 }
352 _ => return,
353 };
354
355 cx.emit_span_lint(INVALID_NAN_COMPARISONS, e.span, lint);
356}
357
358#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ComparisonOp {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::BinOp(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "BinOp",
&__self_0),
Self::Other => ::core::fmt::Formatter::write_str(f, "Other"),
}
}
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for ComparisonOp { }
#[automatically_derived]
impl ::core::cmp::PartialEq for ComparisonOp {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other) &&
match (self, other) {
(Self::BinOp(__self_0), Self::BinOp(__arg1_0)) =>
__self_0 == __arg1_0,
_ => true,
}
}
}PartialEq, #[automatically_derived]
impl ::core::marker::Copy for ComparisonOp { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ComparisonOp { }
#[automatically_derived]
impl ::core::clone::Clone for ComparisonOp {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<hir::BinOpKind>;
*self
}
}Clone)]
359enum ComparisonOp {
360 BinOp(hir::BinOpKind),
361 Other,
362}
363
364fn lint_wide_pointer<'tcx>(
365 cx: &LateContext<'tcx>,
366 e: &'tcx hir::Expr<'tcx>,
367 cmpop: ComparisonOp,
368 l: &'tcx hir::Expr<'tcx>,
369 r: &'tcx hir::Expr<'tcx>,
370) {
371 let ptr_unsized = |mut ty: Ty<'tcx>| -> Option<(
372 usize,
373 String,
374 bool,
375 )> {
376 let mut refs = 0;
377 while let ty::Ref(_, inner_ty, _) = ty.kind() {
380 ty = *inner_ty;
381 refs += 1;
382 }
383
384 let mut modifiers = String::new();
386 ty = match ty.kind() {
387 ty::RawPtr(ty, _) => *ty,
388 ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::NonNull) => {
389 modifiers.push_str(".as_ptr()");
390 args.type_at(0)
391 }
392 _ => return None,
393 };
394
395 (!ty.is_sized(cx.tcx, cx.typing_env()))
396 .then(|| (refs, modifiers, #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Dynamic(_, _) => true,
_ => false,
}matches!(ty.kind(), ty::Dynamic(_, _))))
397 };
398
399 let l = l.peel_borrows();
401 let r = r.peel_borrows();
402
403 let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else {
404 return;
405 };
406 let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else {
407 return;
408 };
409
410 let Some((l_ty_refs, l_modifiers, l_inner_ty_is_dyn)) = ptr_unsized(l_ty) else {
411 return;
412 };
413 let Some((r_ty_refs, r_modifiers, r_inner_ty_is_dyn)) = ptr_unsized(r_ty) else {
414 return;
415 };
416
417 let Some(krate) = std_or_core(cx) else {
418 return cx.emit_span_lint(
419 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
420 e.span,
421 AmbiguousWidePointerComparisons::Warn,
422 );
423 };
424
425 let (Some(l_span), Some(r_span)) =
426 (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
427 else {
428 return cx.emit_span_lint(
429 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
430 e.span,
431 AmbiguousWidePointerComparisons::Spanless { krate },
432 );
433 };
434
435 let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
436 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));
437 let is_dyn_comparison = l_inner_ty_is_dyn && r_inner_ty_is_dyn;
438 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(..));
439
440 let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
441 let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
442 let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
443
444 let deref_left = &*"*".repeat(l_ty_refs);
445 let deref_right = &*"*".repeat(r_ty_refs);
446
447 let l_modifiers = &*l_modifiers;
448 let r_modifiers = &*r_modifiers;
449
450 cx.emit_span_lint(
451 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
452 e.span,
453 if is_eq_ne {
454 AmbiguousWidePointerComparisons::SpanfulEq {
455 addr_metadata_suggestion: (!is_dyn_comparison).then(|| {
456 AmbiguousWidePointerComparisonsAddrMetadataSuggestion {
457 krate,
458 ne,
459 deref_left,
460 deref_right,
461 l_modifiers,
462 r_modifiers,
463 left,
464 middle,
465 right,
466 }
467 }),
468 addr_suggestion: AmbiguousWidePointerComparisonsAddrSuggestion {
469 krate,
470 ne,
471 deref_left,
472 deref_right,
473 l_modifiers,
474 r_modifiers,
475 left,
476 middle,
477 right,
478 },
479 }
480 } else {
481 AmbiguousWidePointerComparisons::SpanfulCmp {
482 cast_suggestion: AmbiguousWidePointerComparisonsCastSuggestion {
483 deref_left,
484 deref_right,
485 l_modifiers,
486 r_modifiers,
487 paren_left: if l_ty_refs != 0 { ")" } else { "" },
488 paren_right: if r_ty_refs != 0 { ")" } else { "" },
489 left_before: (l_ty_refs != 0).then_some(l_span.shrink_to_lo()),
490 left_after: l_span.shrink_to_hi(),
491 right_before: (r_ty_refs != 0).then_some(r_span.shrink_to_lo()),
492 right_after: r_span.shrink_to_hi(),
493 },
494 expect_suggestion: AmbiguousWidePointerComparisonsExpectSuggestion {
495 paren_left: if via_method_call { "" } else { "(" },
496 paren_right: if via_method_call { "" } else { ")" },
497 before: e.span.shrink_to_lo(),
498 after: e.span.shrink_to_hi(),
499 },
500 }
501 },
502 );
503}
504
505fn lint_fn_pointer<'tcx>(
506 cx: &LateContext<'tcx>,
507 e: &'tcx hir::Expr<'tcx>,
508 cmpop: ComparisonOp,
509 l: &'tcx hir::Expr<'tcx>,
510 r: &'tcx hir::Expr<'tcx>,
511) {
512 let peel_refs = |mut ty: Ty<'tcx>| -> (Ty<'tcx>, usize) {
513 let mut refs = 0;
514
515 while let ty::Ref(_, inner_ty, _) = ty.kind() {
516 ty = *inner_ty;
517 refs += 1;
518 }
519
520 (ty, refs)
521 };
522
523 let l = l.peel_borrows();
525 let r = r.peel_borrows();
526
527 let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else { return };
528 let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else { return };
529
530 let (l_ty, l_ty_refs) = peel_refs(l_ty);
533 let (r_ty, r_ty_refs) = peel_refs(r_ty);
534
535 if l_ty.is_fn() && r_ty.is_fn() {
536 } else if let ty::Adt(l_def, l_args) = l_ty.kind()
538 && let ty::Adt(r_def, r_args) = r_ty.kind()
539 && cx.tcx.is_lang_item(l_def.did(), LangItem::Option)
540 && cx.tcx.is_lang_item(r_def.did(), LangItem::Option)
541 && let Some(l_some_arg) = l_args.get(0)
542 && let Some(r_some_arg) = r_args.get(0)
543 && l_some_arg.expect_ty().is_fn()
544 && r_some_arg.expect_ty().is_fn()
545 {
546 return cx.emit_span_lint(
548 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
549 e.span,
550 UnpredictableFunctionPointerComparisons::Warn,
551 );
552 } else {
553 return;
555 }
556
557 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));
560
561 if !is_eq_ne {
562 return cx.emit_span_lint(
564 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
565 e.span,
566 UnpredictableFunctionPointerComparisons::Warn,
567 );
568 }
569
570 let (Some(l_span), Some(r_span)) =
571 (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
572 else {
573 return cx.emit_span_lint(
575 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
576 e.span,
577 UnpredictableFunctionPointerComparisons::Warn,
578 );
579 };
580
581 let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
582
583 let deref_left = &*"*".repeat(l_ty_refs);
585 let deref_right = &*"*".repeat(r_ty_refs);
586
587 let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
588 let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
589 let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
590
591 let Some(krate) = std_or_core(cx) else {
592 return cx.emit_span_lint(
593 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
594 e.span,
595 UnpredictableFunctionPointerComparisons::Warn,
596 );
597 };
598
599 let sugg =
600 if !r_ty.is_fn_ptr() {
603 let fn_sig = r_ty.fn_sig(cx.tcx);
604
605 UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEqWithCast {
606 krate,
607 ne,
608 fn_sig,
609 deref_left,
610 deref_right,
611 left,
612 middle,
613 right,
614 }
615 } else {
616 UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEq {
617 krate,
618 ne,
619 deref_left,
620 deref_right,
621 left,
622 middle,
623 right,
624 }
625 };
626
627 cx.emit_span_lint(
628 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
629 e.span,
630 UnpredictableFunctionPointerComparisons::Suggestion { sugg },
631 );
632}
633
634impl<'tcx> LateLintPass<'tcx> for TypeLimits {
635 fn check_lit(
636 &mut self,
637 cx: &LateContext<'tcx>,
638 hir_id: HirId,
639 lit: hir::Lit,
640 is_negated_pat: bool,
641 ) {
642 let surrounding_negation = if is_negated_pat {
643 Some(lit.span)
646 } else if let Some(negation_info) = self.last_visited_negation
647 && negation_info.negated_id == hir_id
648 {
649 Some(negation_info.negation_span)
650 } else {
651 None
652 };
653 lint_literal(cx, hir_id, lit.span, &lit, surrounding_negation);
654 }
655
656 fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
657 match e.kind {
658 hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
659 self.last_visited_negation =
660 Some(NegationInfo { negation_span: e.span, negated_id: expr.hir_id });
661 }
662 hir::ExprKind::Binary(binop, ref l, ref r) => {
663 if is_comparison(binop.node) {
664 if !check_limits(cx, binop.node, l, r) {
665 cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
666 } else {
667 lint_nan(cx, e, binop.node, l, r);
668 let cmpop = ComparisonOp::BinOp(binop.node);
669 lint_wide_pointer(cx, e, cmpop, l, r);
670 lint_fn_pointer(cx, e, cmpop, l, r);
671 }
672 }
673 }
674 hir::ExprKind::Call(path, [l, r])
675 if let ExprKind::Path(ref qpath) = path.kind
676 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
677 && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
678 && let Some(cmpop) = diag_item_cmpop(diag_item) =>
679 {
680 lint_wide_pointer(cx, e, cmpop, l, r);
681 lint_fn_pointer(cx, e, cmpop, l, r);
682 }
683 hir::ExprKind::MethodCall(_, l, [r], _)
684 if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
685 && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
686 && let Some(cmpop) = diag_item_cmpop(diag_item) =>
687 {
688 lint_wide_pointer(cx, e, cmpop, l, r);
689 lint_fn_pointer(cx, e, cmpop, l, r);
690 }
691 _ => {}
692 };
693
694 fn is_valid<T: PartialOrd>(binop: hir::BinOpKind, v: T, min: T, max: T) -> bool {
695 match binop {
696 hir::BinOpKind::Lt => v > min && v <= max,
697 hir::BinOpKind::Le => v >= min && v < max,
698 hir::BinOpKind::Gt => v >= min && v < max,
699 hir::BinOpKind::Ge => v > min && v <= max,
700 hir::BinOpKind::Eq | hir::BinOpKind::Ne => v >= min && v <= max,
701 _ => ::rustc_span::macros::bug_impl(None, format_args!("impossible case reached"),
Location::caller())bug!(),
702 }
703 }
704
705 fn rev_binop(binop: hir::BinOpKind) -> hir::BinOpKind {
706 match binop {
707 hir::BinOpKind::Lt => hir::BinOpKind::Gt,
708 hir::BinOpKind::Le => hir::BinOpKind::Ge,
709 hir::BinOpKind::Gt => hir::BinOpKind::Lt,
710 hir::BinOpKind::Ge => hir::BinOpKind::Le,
711 _ => binop,
712 }
713 }
714
715 fn check_limits(
716 cx: &LateContext<'_>,
717 binop: hir::BinOpKind,
718 l: &hir::Expr<'_>,
719 r: &hir::Expr<'_>,
720 ) -> bool {
721 let (lit, expr, swap) = match (&l.kind, &r.kind) {
722 (&hir::ExprKind::Lit(_), _) => (l, r, true),
723 (_, &hir::ExprKind::Lit(_)) => (r, l, false),
724 _ => return true,
725 };
726 let norm_binop = if swap { rev_binop(binop) } else { binop };
729 match *cx.typeck_results().node_type(expr.hir_id).kind() {
730 ty::Int(int_ty) => {
731 let (min, max) = int_ty_range(int_ty);
732 let lit_val: i128 = match lit.kind {
733 hir::ExprKind::Lit(li) => match li.node {
734 ast::LitKind::Int(
735 v,
736 ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed,
737 ) => v.get() as i128,
738 _ => return true,
739 },
740 _ => ::rustc_span::macros::bug_impl(None, format_args!("impossible case reached"),
Location::caller())bug!(),
741 };
742 is_valid(norm_binop, lit_val, min, max)
743 }
744 ty::Uint(uint_ty) => {
745 let (min, max): (u128, u128) = uint_ty_range(uint_ty);
746 let lit_val: u128 = match lit.kind {
747 hir::ExprKind::Lit(li) => match li.node {
748 ast::LitKind::Int(v, _) => v.get(),
749 _ => return true,
750 },
751 _ => ::rustc_span::macros::bug_impl(None, format_args!("impossible case reached"),
Location::caller())bug!(),
752 };
753 is_valid(norm_binop, lit_val, min, max)
754 }
755 _ => true,
756 }
757 }
758
759 fn is_comparison(binop: hir::BinOpKind) -> bool {
760 #[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!(
761 binop,
762 hir::BinOpKind::Eq
763 | hir::BinOpKind::Lt
764 | hir::BinOpKind::Le
765 | hir::BinOpKind::Ne
766 | hir::BinOpKind::Ge
767 | hir::BinOpKind::Gt
768 )
769 }
770
771 fn diag_item_cmpop(diag_item: Symbol) -> Option<ComparisonOp> {
772 Some(match diag_item {
773 sym::cmp_ord_max => ComparisonOp::Other,
774 sym::cmp_ord_min => ComparisonOp::Other,
775 sym::ord_cmp_method => ComparisonOp::Other,
776 sym::cmp_partialeq_eq => ComparisonOp::BinOp(hir::BinOpKind::Eq),
777 sym::cmp_partialeq_ne => ComparisonOp::BinOp(hir::BinOpKind::Ne),
778 sym::cmp_partialord_cmp => ComparisonOp::Other,
779 sym::cmp_partialord_ge => ComparisonOp::BinOp(hir::BinOpKind::Ge),
780 sym::cmp_partialord_gt => ComparisonOp::BinOp(hir::BinOpKind::Gt),
781 sym::cmp_partialord_le => ComparisonOp::BinOp(hir::BinOpKind::Le),
782 sym::cmp_partialord_lt => ComparisonOp::BinOp(hir::BinOpKind::Lt),
783 _ => return None,
784 })
785 }
786 }
787}
788
789pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
790 tcx: TyCtxt<'tcx>,
791 def: ty::AdtDef<'tcx>,
792) -> bool {
793 {
{
'done:
{
for i in ::rustc_attr_ir::HasAttrs::get_attrs(def.did(), &tcx)
{
#[allow(unused_imports)]
use ::rustc_attr_ir::AttributeKind::*;
let i: &::rustc_attr_ir::Attribute = i;
match i {
::rustc_attr_ir::Attribute::Parsed(RustcNonnullOptimizationGuaranteed)
=> {
break 'done Some(());
}
::rustc_attr_ir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}
}.is_some()find_attr!(tcx, def.did(), RustcNonnullOptimizationGuaranteed)
794}
795
796pub(crate) fn transparent_newtype_field<'a, 'tcx>(
799 tcx: TyCtxt<'tcx>,
800 variant: &'a ty::VariantDef,
801) -> Option<&'a ty::FieldDef> {
802 let typing_env = ty::TypingEnv::non_body_analysis(tcx, variant.def_id);
803 variant.fields.iter().find(|field| {
804 let field_ty = tcx.type_of(field.did).instantiate_identity().skip_norm_wip();
805 let is_1zst =
806 tcx.layout_of(typing_env.as_query_input(field_ty)).is_ok_and(|layout| layout.is_1zst());
807 !is_1zst
808 })
809}
810
811fn ty_is_known_nonnull<'tcx>(
813 tcx: TyCtxt<'tcx>,
814 typing_env: ty::TypingEnv<'tcx>,
815 ty: Ty<'tcx>,
816) -> bool {
817 let ty = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty);
818
819 match ty.kind() {
820 ty::FnPtr(..) => true,
821 ty::Ref(..) => true,
822 ty::Adt(def, _) if def.is_box() => true,
823 ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => {
824 let marked_non_null = nonnull_optimization_guaranteed(tcx, *def);
825
826 if marked_non_null {
827 return true;
828 }
829
830 if def.is_unsafe_cell() || def.is_unsafe_pinned() {
832 return false;
833 }
834
835 def.variants().iter().filter_map(|variant| transparent_newtype_field(tcx, variant)).any(
836 |field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args).skip_norm_wip()),
837 )
838 }
839 ty::Pat(base, pat) => {
840 ty_is_known_nonnull(tcx, typing_env, *base)
841 || pat_ty_is_known_nonnull(tcx, typing_env, *pat)
842 }
843 _ => false,
844 }
845}
846
847fn pat_ty_is_known_nonnull<'tcx>(
848 tcx: TyCtxt<'tcx>,
849 typing_env: ty::TypingEnv<'tcx>,
850 pat: ty::Pattern<'tcx>,
851) -> bool {
852 try {
853 match *pat {
854 ty::PatternKind::Range { start, end } => {
855 let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?;
856 let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?;
857
858 start > 0 && end >= start
861 }
862 ty::PatternKind::NotNull => true,
863 ty::PatternKind::Or(patterns) => {
864 patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat))
865 }
866 }
867 }
868 .unwrap_or_default()
869}
870
871fn get_nullable_type<'tcx>(
874 tcx: TyCtxt<'tcx>,
875 typing_env: ty::TypingEnv<'tcx>,
876 ty: Ty<'tcx>,
877) -> Option<Ty<'tcx>> {
878 let ty = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty);
879
880 Some(match *ty.kind() {
881 ty::Adt(field_def, field_args) => {
882 let inner_field_ty = {
883 let mut first_non_zst_ty =
884 field_def.variants().iter().filter_map(|v| transparent_newtype_field(tcx, v));
885 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!(
886 first_non_zst_ty.clone().count(),
887 1,
888 "Wrong number of fields for transparent type"
889 );
890 first_non_zst_ty
891 .next_back()
892 .expect("No non-zst fields in transparent type.")
893 .ty(tcx, field_args)
894 .skip_norm_wip()
895 };
896 return get_nullable_type(tcx, typing_env, inner_field_ty);
897 }
898 ty::Pat(base, ..) => return get_nullable_type(tcx, typing_env, base),
899 ty::Int(_) | ty::Uint(_) | ty::Char | ty::RawPtr(..) => ty,
900 ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl),
903 ty::FnPtr(..) => ty,
906 ref unhandled => {
909 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/7189d39d9ce936881b8132c6addeaf3e295cd455/compiler/rustc_lint/src/types.rs:909",
"rustc_lint::types", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/7189d39d9ce936881b8132c6addeaf3e295cd455/compiler/rustc_lint/src/types.rs"),
::tracing_core::__macro_support::Option::Some(909u32),
::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!(
910 "get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}",
911 unhandled, ty
912 );
913 return None;
914 }
915 })
916}
917
918fn is_niche_optimization_candidate<'tcx>(
923 tcx: TyCtxt<'tcx>,
924 typing_env: ty::TypingEnv<'tcx>,
925 ty: Ty<'tcx>,
926) -> bool {
927 if tcx.layout_of(typing_env.as_query_input(ty)).is_ok_and(|layout| !layout.is_1zst()) {
928 return false;
929 }
930
931 match ty.kind() {
932 ty::Adt(ty_def, _) => {
933 let non_exhaustive = ty_def.is_variant_list_non_exhaustive();
934 let empty = (ty_def.is_struct() && ty_def.non_enum_variant().fields.is_empty())
935 || (ty_def.is_enum() && ty_def.variants().is_empty());
936
937 !non_exhaustive && empty
938 }
939 ty::Tuple(tys) => tys.is_empty(),
940 _ => false,
941 }
942}
943
944pub(crate) fn repr_nullable_ptr<'tcx>(
949 tcx: TyCtxt<'tcx>,
950 typing_env: ty::TypingEnv<'tcx>,
951 ty: Ty<'tcx>,
952) -> Option<Ty<'tcx>> {
953 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/7189d39d9ce936881b8132c6addeaf3e295cd455/compiler/rustc_lint/src/types.rs:953",
"rustc_lint::types", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/7189d39d9ce936881b8132c6addeaf3e295cd455/compiler/rustc_lint/src/types.rs"),
::tracing_core::__macro_support::Option::Some(953u32),
::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);
954 match ty.kind() {
955 ty::Adt(ty_def, args) => {
956 let field_ty = match &ty_def.variants().raw[..] {
957 [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
958 ([], [field]) | ([field], []) => field.ty(tcx, args).skip_norm_wip(),
959 ([field1], [field2]) => {
960 let ty1 = field1.ty(tcx, args).skip_norm_wip();
961 let ty2 = field2.ty(tcx, args).skip_norm_wip();
962
963 if is_niche_optimization_candidate(tcx, typing_env, ty1) {
964 ty2
965 } else if is_niche_optimization_candidate(tcx, typing_env, ty2) {
966 ty1
967 } else {
968 return None;
969 }
970 }
971 _ => return None,
972 },
973 _ => return None,
974 };
975
976 if !ty_is_known_nonnull(tcx, typing_env, field_ty) {
977 return None;
978 }
979
980 let compute_size_skeleton =
984 |t| SizeSkeleton::compute(t, tcx, typing_env, DUMMY_SP).ok();
985 if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) {
986 ::rustc_span::macros::bug_impl(None,
format_args!("improper_ctypes: Option nonnull optimization not applied?"),
Location::caller());bug!("improper_ctypes: Option nonnull optimization not applied?");
987 }
988
989 let field_ty_layout = tcx.layout_of(typing_env.as_query_input(field_ty));
991 if field_ty_layout.is_err() && !field_ty.has_non_region_param() {
992 ::rustc_span::macros::bug_impl(None,
format_args!("should be able to compute the layout of non-polymorphic type"),
Location::caller());bug!("should be able to compute the layout of non-polymorphic type");
993 }
994
995 let field_ty_abi = &field_ty_layout.ok()?.backend_repr;
996 if let BackendRepr::Scalar(field_ty_scalar) = field_ty_abi {
997 match field_ty_scalar.valid_range(&tcx) {
998 WrappingRange { start: 0, end }
999 if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 =>
1000 {
1001 return Some(get_nullable_type(tcx, typing_env, field_ty).expect(
1002 "known non-null scalar type should have a nullable representation",
1003 ));
1004 }
1005 WrappingRange { start: 1, .. } => {
1006 return Some(get_nullable_type(tcx, typing_env, field_ty).expect(
1007 "known non-null scalar type should have a nullable representation",
1008 ));
1009 }
1010 WrappingRange { start, end } => {
1011 {
::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)
1012 }
1013 };
1014 }
1015 None
1016 }
1017 ty::Pat(base, pat) => get_nullable_type_from_pat(tcx, typing_env, *base, *pat),
1018 _ => None,
1019 }
1020}
1021
1022fn get_nullable_type_from_pat<'tcx>(
1023 tcx: TyCtxt<'tcx>,
1024 typing_env: ty::TypingEnv<'tcx>,
1025 base: Ty<'tcx>,
1026 pat: ty::Pattern<'tcx>,
1027) -> Option<Ty<'tcx>> {
1028 match *pat {
1029 ty::PatternKind::NotNull | ty::PatternKind::Range { .. } => {
1030 get_nullable_type(tcx, typing_env, base)
1031 }
1032 ty::PatternKind::Or(patterns) => {
1033 let first = get_nullable_type_from_pat(tcx, typing_env, base, patterns[0])?;
1034 for &pat in &patterns[1..] {
1035 {
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)?);
1036 }
1037 Some(first)
1038 }
1039 }
1040}
1041
1042pub 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) -> Self { *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]);
1043
1044impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
1045 fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
1046 if let hir::ItemKind::Enum(_, _, ref enum_definition) = it.kind {
1047 let t = cx.tcx.type_of(it.owner_id).instantiate_identity().skip_norm_wip();
1048 let ty = cx.tcx.erase_and_anonymize_regions(t);
1049 let Ok(layout) = cx.layout_of(ty) else { return };
1050 let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, variants, .. } =
1051 &layout.variants
1052 else {
1053 return;
1054 };
1055
1056 let tag_size = tag.size(&cx.tcx).bytes();
1057
1058 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/7189d39d9ce936881b8132c6addeaf3e295cd455/compiler/rustc_lint/src/types.rs:1058",
"rustc_lint::types", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/7189d39d9ce936881b8132c6addeaf3e295cd455/compiler/rustc_lint/src/types.rs"),
::tracing_core::__macro_support::Option::Some(1058u32),
::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!(
1059 "enum `{}` is {} bytes large with layout:\n{:#?}",
1060 t,
1061 layout.size.bytes(),
1062 layout
1063 );
1064
1065 let (largest, slargest, largest_index) = iter::zip(enum_definition.variants, variants)
1066 .map(|(variant, variant_layout)| {
1067 let bytes = variant_layout.size.bytes().saturating_sub(tag_size);
1069
1070 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/7189d39d9ce936881b8132c6addeaf3e295cd455/compiler/rustc_lint/src/types.rs:1070",
"rustc_lint::types", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/7189d39d9ce936881b8132c6addeaf3e295cd455/compiler/rustc_lint/src/types.rs"),
::tracing_core::__macro_support::Option::Some(1070u32),
::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);
1071 bytes
1072 })
1073 .enumerate()
1074 .fold((0, 0, 0), |(l, s, li), (idx, size)| {
1075 if size > l {
1076 (size, l, idx)
1077 } else if size > s {
1078 (l, size, li)
1079 } else {
1080 (l, s, li)
1081 }
1082 });
1083
1084 if largest > slargest * 3 && slargest > 0 {
1087 cx.emit_span_lint(
1088 VARIANT_SIZE_DIFFERENCES,
1089 enum_definition.variants[largest_index].span,
1090 VariantSizeDifferencesDiag { largest },
1091 );
1092 }
1093 }
1094 }
1095}
1096
1097#[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",
..
};declare_lint! {
1098 INVALID_ATOMIC_ORDERING,
1136 Deny,
1137 "usage of invalid atomic ordering in atomic operations and memory fences"
1138}
1139
1140pub 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) -> Self { *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]);
1141
1142impl InvalidAtomicOrdering {
1143 fn inherent_atomic_method_call<'hir>(
1144 cx: &LateContext<'_>,
1145 expr: &Expr<'hir>,
1146 recognized_names: &[Symbol], ) -> Option<(Symbol, &'hir [Expr<'hir>])> {
1148 if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind
1149 && recognized_names.contains(&method_path.ident.name)
1150 && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
1151 && let Some(impl_did) = cx.tcx.inherent_impl_of_assoc(m_def_id)
1153 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().skip_norm_wip().ty_adt_def()
1154 && cx.tcx.is_diagnostic_item(sym::Atomic, adt.did())
1155 {
1156 return Some((method_path.ident.name, args));
1157 }
1158 None
1159 }
1160
1161 fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol> {
1162 let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None };
1163 let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?;
1164 let tcx = cx.tcx;
1165 let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering);
1166 let name = tcx.item_name(did);
1167 let parent = tcx.parent(did);
1168 [sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find(
1169 |&ordering| {
1170 name == ordering
1171 && (Some(parent) == atomic_ordering
1172 || tcx.opt_parent(parent) == atomic_ordering)
1174 },
1175 )
1176 }
1177
1178 fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
1179 if let Some((method, args)) =
1180 Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
1181 && let Some((ordering_arg, invalid_ordering)) = match method {
1182 sym::load => Some((&args[0], sym::Release)),
1183 sym::store => Some((&args[1], sym::Acquire)),
1184 _ => None,
1185 }
1186 && let Some(ordering) = Self::match_ordering(cx, ordering_arg)
1187 && (ordering == invalid_ordering || ordering == sym::AcqRel)
1188 {
1189 if method == sym::load {
1190 cx.emit_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad);
1191 } else {
1192 cx.emit_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore);
1193 };
1194 }
1195 }
1196
1197 fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
1198 if let ExprKind::Call(func, args) = expr.kind
1199 && let ExprKind::Path(ref func_qpath) = func.kind
1200 && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
1201 && #[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))
1202 && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
1203 {
1204 cx.emit_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence);
1205 }
1206 }
1207
1208 fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
1209 let Some((method, args)) = Self::inherent_atomic_method_call(
1210 cx,
1211 expr,
1212 &[
1213 sym::update,
1214 sym::try_update,
1215 sym::fetch_update,
1216 sym::compare_exchange,
1217 sym::compare_exchange_weak,
1218 ],
1219 ) else {
1220 return;
1221 };
1222
1223 let fail_order_arg = match method {
1224 sym::update | sym::try_update | sym::fetch_update => &args[1],
1225 sym::compare_exchange | sym::compare_exchange_weak => &args[3],
1226 _ => return,
1227 };
1228
1229 let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
1230
1231 if #[allow(non_exhaustive_omitted_patterns)] match fail_ordering {
sym::Release | sym::AcqRel => true,
_ => false,
}matches!(fail_ordering, sym::Release | sym::AcqRel) {
1232 cx.emit_span_lint(
1233 INVALID_ATOMIC_ORDERING,
1234 fail_order_arg.span,
1235 InvalidAtomicOrderingDiag { method, fail_order_arg_span: fail_order_arg.span },
1236 );
1237 }
1238 }
1239}
1240
1241impl<'tcx> LateLintPass<'tcx> for InvalidAtomicOrdering {
1242 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1243 Self::check_atomic_load_store(cx, expr);
1244 Self::check_memory_fence(cx, expr);
1245 Self::check_atomic_compare_exchange(cx, expr);
1246 }
1247}