1use std::iter;
2use std::ops::ControlFlow;
3
4use rustc_abi::{BackendRepr, TagEncoding, VariantIdx, Variants, WrappingRange};
5use rustc_data_structures::fx::FxHashSet;
6use rustc_errors::DiagMessage;
7use rustc_hir::intravisit::VisitorExt;
8use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem};
9use rustc_middle::bug;
10use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton};
11use rustc_middle::ty::{
12 self, Adt, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
13 TypeVisitableExt,
14};
15use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
16use rustc_span::def_id::LocalDefId;
17use rustc_span::{Span, Symbol, sym};
18use tracing::debug;
19use {rustc_ast as ast, rustc_hir as hir};
20
21mod improper_ctypes;
22
23use crate::lints::{
24 AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
25 AmbiguousWidePointerComparisonsAddrSuggestion, AmbiguousWidePointerComparisonsCastSuggestion,
26 AmbiguousWidePointerComparisonsExpectSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
27 AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
28 InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
29 UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, UsesPowerAlignment,
30 VariantSizeDifferencesDiag,
31};
32use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
33
34mod literal;
35
36use literal::{int_ty_range, lint_literal, uint_ty_range};
37
38declare_lint! {
39 UNUSED_COMPARISONS,
57 Warn,
58 "comparisons made useless by limits of the types involved"
59}
60
61declare_lint! {
62 OVERFLOWING_LITERALS,
79 Deny,
80 "literal out of range for its type"
81}
82
83declare_lint! {
84 VARIANT_SIZE_DIFFERENCES,
116 Allow,
117 "detects enums with widely varying variant sizes"
118}
119
120declare_lint! {
121 INVALID_NAN_COMPARISONS,
138 Warn,
139 "detects invalid floating point NaN comparisons"
140}
141
142declare_lint! {
143 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
169 Warn,
170 "detects ambiguous wide pointer comparisons"
171}
172
173declare_lint! {
174 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
198 Warn,
199 "detects unpredictable function pointer comparisons",
200 report_in_external_macro
201}
202
203#[derive(Copy, Clone, Default)]
204pub(crate) struct TypeLimits {
205 negated_expr_id: Option<hir::HirId>,
207 negated_expr_span: Option<Span>,
209}
210
211impl_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 { negated_expr_id: None, negated_expr_span: 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 is_nan(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
233 let expr = expr.peel_blocks().peel_borrows();
234 match expr.kind {
235 ExprKind::Path(qpath) => {
236 let Some(def_id) = cx.typeck_results().qpath_res(&qpath, expr.hir_id).opt_def_id()
237 else {
238 return false;
239 };
240
241 matches!(
242 cx.tcx.get_diagnostic_name(def_id),
243 Some(sym::f16_nan | sym::f32_nan | sym::f64_nan | sym::f128_nan)
244 )
245 }
246 _ => false,
247 }
248 }
249
250 fn eq_ne(
251 e: &hir::Expr<'_>,
252 l: &hir::Expr<'_>,
253 r: &hir::Expr<'_>,
254 f: impl FnOnce(Span, Span) -> InvalidNanComparisonsSuggestion,
255 ) -> InvalidNanComparisons {
256 let suggestion = if let Some(l_span) = l.span.find_ancestor_inside(e.span)
257 && let Some(r_span) = r.span.find_ancestor_inside(e.span)
258 {
259 f(l_span, r_span)
260 } else {
261 InvalidNanComparisonsSuggestion::Spanless
262 };
263
264 InvalidNanComparisons::EqNe { suggestion }
265 }
266
267 let lint = match binop {
268 hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => {
269 eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
270 nan_plus_binop: l_span.until(r_span),
271 float: r_span.shrink_to_hi(),
272 neg: (binop == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
273 })
274 }
275 hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => {
276 eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
277 nan_plus_binop: l_span.shrink_to_hi().to(r_span),
278 float: l_span.shrink_to_hi(),
279 neg: (binop == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
280 })
281 }
282 hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge
283 if is_nan(cx, l) || is_nan(cx, r) =>
284 {
285 InvalidNanComparisons::LtLeGtGe
286 }
287 _ => return,
288 };
289
290 cx.emit_span_lint(INVALID_NAN_COMPARISONS, e.span, lint);
291}
292
293#[derive(Debug, PartialEq, Copy, Clone)]
294enum ComparisonOp {
295 BinOp(hir::BinOpKind),
296 Other,
297}
298
299fn lint_wide_pointer<'tcx>(
300 cx: &LateContext<'tcx>,
301 e: &'tcx hir::Expr<'tcx>,
302 cmpop: ComparisonOp,
303 l: &'tcx hir::Expr<'tcx>,
304 r: &'tcx hir::Expr<'tcx>,
305) {
306 let ptr_unsized = |mut ty: Ty<'tcx>| -> Option<(
307 usize,
308 String,
309 bool,
310 )> {
311 let mut refs = 0;
312 while let ty::Ref(_, inner_ty, _) = ty.kind() {
315 ty = *inner_ty;
316 refs += 1;
317 }
318
319 let mut modifiers = String::new();
321 ty = match ty.kind() {
322 ty::RawPtr(ty, _) => *ty,
323 ty::Adt(def, args) if cx.tcx.is_diagnostic_item(sym::NonNull, def.did()) => {
324 modifiers.push_str(".as_ptr()");
325 args.type_at(0)
326 }
327 _ => return None,
328 };
329
330 (!ty.is_sized(cx.tcx, cx.typing_env()))
331 .then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn))))
332 };
333
334 let l = l.peel_borrows();
336 let r = r.peel_borrows();
337
338 let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else {
339 return;
340 };
341 let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else {
342 return;
343 };
344
345 let Some((l_ty_refs, l_modifiers, l_inner_ty_is_dyn)) = ptr_unsized(l_ty) else {
346 return;
347 };
348 let Some((r_ty_refs, r_modifiers, r_inner_ty_is_dyn)) = ptr_unsized(r_ty) else {
349 return;
350 };
351
352 let (Some(l_span), Some(r_span)) =
353 (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
354 else {
355 return cx.emit_span_lint(
356 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
357 e.span,
358 AmbiguousWidePointerComparisons::Spanless,
359 );
360 };
361
362 let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
363 let is_eq_ne = matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne));
364 let is_dyn_comparison = l_inner_ty_is_dyn && r_inner_ty_is_dyn;
365 let via_method_call = matches!(&e.kind, ExprKind::MethodCall(..) | ExprKind::Call(..));
366
367 let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
368 let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
369 let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
370
371 let deref_left = &*"*".repeat(l_ty_refs);
372 let deref_right = &*"*".repeat(r_ty_refs);
373
374 let l_modifiers = &*l_modifiers;
375 let r_modifiers = &*r_modifiers;
376
377 cx.emit_span_lint(
378 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
379 e.span,
380 if is_eq_ne {
381 AmbiguousWidePointerComparisons::SpanfulEq {
382 addr_metadata_suggestion: (!is_dyn_comparison).then(|| {
383 AmbiguousWidePointerComparisonsAddrMetadataSuggestion {
384 ne,
385 deref_left,
386 deref_right,
387 l_modifiers,
388 r_modifiers,
389 left,
390 middle,
391 right,
392 }
393 }),
394 addr_suggestion: AmbiguousWidePointerComparisonsAddrSuggestion {
395 ne,
396 deref_left,
397 deref_right,
398 l_modifiers,
399 r_modifiers,
400 left,
401 middle,
402 right,
403 },
404 }
405 } else {
406 AmbiguousWidePointerComparisons::SpanfulCmp {
407 cast_suggestion: AmbiguousWidePointerComparisonsCastSuggestion {
408 deref_left,
409 deref_right,
410 l_modifiers,
411 r_modifiers,
412 paren_left: if l_ty_refs != 0 { ")" } else { "" },
413 paren_right: if r_ty_refs != 0 { ")" } else { "" },
414 left_before: (l_ty_refs != 0).then_some(l_span.shrink_to_lo()),
415 left_after: l_span.shrink_to_hi(),
416 right_before: (r_ty_refs != 0).then_some(r_span.shrink_to_lo()),
417 right_after: r_span.shrink_to_hi(),
418 },
419 expect_suggestion: AmbiguousWidePointerComparisonsExpectSuggestion {
420 paren_left: if via_method_call { "" } else { "(" },
421 paren_right: if via_method_call { "" } else { ")" },
422 before: e.span.shrink_to_lo(),
423 after: e.span.shrink_to_hi(),
424 },
425 }
426 },
427 );
428}
429
430fn lint_fn_pointer<'tcx>(
431 cx: &LateContext<'tcx>,
432 e: &'tcx hir::Expr<'tcx>,
433 cmpop: ComparisonOp,
434 l: &'tcx hir::Expr<'tcx>,
435 r: &'tcx hir::Expr<'tcx>,
436) {
437 let peel_refs = |mut ty: Ty<'tcx>| -> (Ty<'tcx>, usize) {
438 let mut refs = 0;
439
440 while let ty::Ref(_, inner_ty, _) = ty.kind() {
441 ty = *inner_ty;
442 refs += 1;
443 }
444
445 (ty, refs)
446 };
447
448 let l = l.peel_borrows();
450 let r = r.peel_borrows();
451
452 let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else { return };
453 let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else { return };
454
455 let (l_ty, l_ty_refs) = peel_refs(l_ty);
458 let (r_ty, r_ty_refs) = peel_refs(r_ty);
459
460 if l_ty.is_fn() && r_ty.is_fn() {
461 } else if let ty::Adt(l_def, l_args) = l_ty.kind()
463 && let ty::Adt(r_def, r_args) = r_ty.kind()
464 && cx.tcx.is_lang_item(l_def.did(), LangItem::Option)
465 && cx.tcx.is_lang_item(r_def.did(), LangItem::Option)
466 && let Some(l_some_arg) = l_args.get(0)
467 && let Some(r_some_arg) = r_args.get(0)
468 && l_some_arg.expect_ty().is_fn()
469 && r_some_arg.expect_ty().is_fn()
470 {
471 return cx.emit_span_lint(
473 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
474 e.span,
475 UnpredictableFunctionPointerComparisons::Warn,
476 );
477 } else {
478 return;
480 }
481
482 let is_eq_ne = matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne));
485
486 if !is_eq_ne {
487 return cx.emit_span_lint(
489 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
490 e.span,
491 UnpredictableFunctionPointerComparisons::Warn,
492 );
493 }
494
495 let (Some(l_span), Some(r_span)) =
496 (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
497 else {
498 return cx.emit_span_lint(
500 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
501 e.span,
502 UnpredictableFunctionPointerComparisons::Warn,
503 );
504 };
505
506 let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
507
508 let deref_left = &*"*".repeat(l_ty_refs);
510 let deref_right = &*"*".repeat(r_ty_refs);
511
512 let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
513 let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
514 let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
515
516 let sugg =
517 if !r_ty.is_fn_ptr() {
520 let fn_sig = r_ty.fn_sig(cx.tcx);
521
522 UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEqWithCast {
523 ne,
524 fn_sig,
525 deref_left,
526 deref_right,
527 left,
528 middle,
529 right,
530 }
531 } else {
532 UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEq {
533 ne,
534 deref_left,
535 deref_right,
536 left,
537 middle,
538 right,
539 }
540 };
541
542 cx.emit_span_lint(
543 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
544 e.span,
545 UnpredictableFunctionPointerComparisons::Suggestion { sugg },
546 );
547}
548
549impl<'tcx> LateLintPass<'tcx> for TypeLimits {
550 fn check_lit(
551 &mut self,
552 cx: &LateContext<'tcx>,
553 hir_id: HirId,
554 lit: &'tcx hir::Lit,
555 negated: bool,
556 ) {
557 if negated {
558 self.negated_expr_id = Some(hir_id);
559 self.negated_expr_span = Some(lit.span);
560 }
561 lint_literal(cx, self, hir_id, lit.span, lit, negated);
562 }
563
564 fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
565 match e.kind {
566 hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
567 if self.negated_expr_id != Some(e.hir_id) {
569 self.negated_expr_id = Some(expr.hir_id);
570 self.negated_expr_span = Some(e.span);
571 }
572 }
573 hir::ExprKind::Binary(binop, ref l, ref r) => {
574 if is_comparison(binop.node) {
575 if !check_limits(cx, binop.node, l, r) {
576 cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
577 } else {
578 lint_nan(cx, e, binop.node, l, r);
579 let cmpop = ComparisonOp::BinOp(binop.node);
580 lint_wide_pointer(cx, e, cmpop, l, r);
581 lint_fn_pointer(cx, e, cmpop, l, r);
582 }
583 }
584 }
585 hir::ExprKind::Call(path, [l, r])
586 if let ExprKind::Path(ref qpath) = path.kind
587 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
588 && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
589 && let Some(cmpop) = diag_item_cmpop(diag_item) =>
590 {
591 lint_wide_pointer(cx, e, cmpop, l, r);
592 lint_fn_pointer(cx, e, cmpop, l, r);
593 }
594 hir::ExprKind::MethodCall(_, l, [r], _)
595 if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
596 && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
597 && let Some(cmpop) = diag_item_cmpop(diag_item) =>
598 {
599 lint_wide_pointer(cx, e, cmpop, l, r);
600 lint_fn_pointer(cx, e, cmpop, l, r);
601 }
602 _ => {}
603 };
604
605 fn is_valid<T: PartialOrd>(binop: hir::BinOpKind, v: T, min: T, max: T) -> bool {
606 match binop {
607 hir::BinOpKind::Lt => v > min && v <= max,
608 hir::BinOpKind::Le => v >= min && v < max,
609 hir::BinOpKind::Gt => v >= min && v < max,
610 hir::BinOpKind::Ge => v > min && v <= max,
611 hir::BinOpKind::Eq | hir::BinOpKind::Ne => v >= min && v <= max,
612 _ => bug!(),
613 }
614 }
615
616 fn rev_binop(binop: hir::BinOpKind) -> hir::BinOpKind {
617 match binop {
618 hir::BinOpKind::Lt => hir::BinOpKind::Gt,
619 hir::BinOpKind::Le => hir::BinOpKind::Ge,
620 hir::BinOpKind::Gt => hir::BinOpKind::Lt,
621 hir::BinOpKind::Ge => hir::BinOpKind::Le,
622 _ => binop,
623 }
624 }
625
626 fn check_limits(
627 cx: &LateContext<'_>,
628 binop: hir::BinOpKind,
629 l: &hir::Expr<'_>,
630 r: &hir::Expr<'_>,
631 ) -> bool {
632 let (lit, expr, swap) = match (&l.kind, &r.kind) {
633 (&hir::ExprKind::Lit(_), _) => (l, r, true),
634 (_, &hir::ExprKind::Lit(_)) => (r, l, false),
635 _ => return true,
636 };
637 let norm_binop = if swap { rev_binop(binop) } else { binop };
640 match *cx.typeck_results().node_type(expr.hir_id).kind() {
641 ty::Int(int_ty) => {
642 let (min, max) = int_ty_range(int_ty);
643 let lit_val: i128 = match lit.kind {
644 hir::ExprKind::Lit(li) => match li.node {
645 ast::LitKind::Int(
646 v,
647 ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed,
648 ) => v.get() as i128,
649 _ => return true,
650 },
651 _ => bug!(),
652 };
653 is_valid(norm_binop, lit_val, min, max)
654 }
655 ty::Uint(uint_ty) => {
656 let (min, max): (u128, u128) = uint_ty_range(uint_ty);
657 let lit_val: u128 = match lit.kind {
658 hir::ExprKind::Lit(li) => match li.node {
659 ast::LitKind::Int(v, _) => v.get(),
660 _ => return true,
661 },
662 _ => bug!(),
663 };
664 is_valid(norm_binop, lit_val, min, max)
665 }
666 _ => true,
667 }
668 }
669
670 fn is_comparison(binop: hir::BinOpKind) -> bool {
671 matches!(
672 binop,
673 hir::BinOpKind::Eq
674 | hir::BinOpKind::Lt
675 | hir::BinOpKind::Le
676 | hir::BinOpKind::Ne
677 | hir::BinOpKind::Ge
678 | hir::BinOpKind::Gt
679 )
680 }
681
682 fn diag_item_cmpop(diag_item: Symbol) -> Option<ComparisonOp> {
683 Some(match diag_item {
684 sym::cmp_ord_max => ComparisonOp::Other,
685 sym::cmp_ord_min => ComparisonOp::Other,
686 sym::ord_cmp_method => ComparisonOp::Other,
687 sym::cmp_partialeq_eq => ComparisonOp::BinOp(hir::BinOpKind::Eq),
688 sym::cmp_partialeq_ne => ComparisonOp::BinOp(hir::BinOpKind::Ne),
689 sym::cmp_partialord_cmp => ComparisonOp::Other,
690 sym::cmp_partialord_ge => ComparisonOp::BinOp(hir::BinOpKind::Ge),
691 sym::cmp_partialord_gt => ComparisonOp::BinOp(hir::BinOpKind::Gt),
692 sym::cmp_partialord_le => ComparisonOp::BinOp(hir::BinOpKind::Le),
693 sym::cmp_partialord_lt => ComparisonOp::BinOp(hir::BinOpKind::Lt),
694 _ => return None,
695 })
696 }
697 }
698}
699
700declare_lint! {
701 IMPROPER_CTYPES,
723 Warn,
724 "proper use of libc types in foreign modules"
725}
726
727declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]);
728
729declare_lint! {
730 IMPROPER_CTYPES_DEFINITIONS,
752 Warn,
753 "proper use of libc types in foreign item definitions"
754}
755
756declare_lint! {
757 USES_POWER_ALIGNMENT,
807 Warn,
808 "Structs do not follow the power alignment rule under repr(C)"
809}
810
811declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS, USES_POWER_ALIGNMENT]);
812
813#[derive(Clone, Copy)]
814pub(crate) enum CItemKind {
815 Declaration,
816 Definition,
817}
818
819struct ImproperCTypesVisitor<'a, 'tcx> {
820 cx: &'a LateContext<'tcx>,
821 mode: CItemKind,
822}
823
824struct CTypesVisitorState<'tcx> {
826 cache: FxHashSet<Ty<'tcx>>,
827 base_ty: Ty<'tcx>,
830}
831
832enum FfiResult<'tcx> {
833 FfiSafe,
834 FfiPhantom(Ty<'tcx>),
835 FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> },
836}
837
838pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
839 tcx: TyCtxt<'tcx>,
840 def: ty::AdtDef<'tcx>,
841) -> bool {
842 tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed)
843}
844
845pub(crate) fn transparent_newtype_field<'a, 'tcx>(
848 tcx: TyCtxt<'tcx>,
849 variant: &'a ty::VariantDef,
850) -> Option<&'a ty::FieldDef> {
851 let typing_env = ty::TypingEnv::non_body_analysis(tcx, variant.def_id);
852 variant.fields.iter().find(|field| {
853 let field_ty = tcx.type_of(field.did).instantiate_identity();
854 let is_1zst =
855 tcx.layout_of(typing_env.as_query_input(field_ty)).is_ok_and(|layout| layout.is_1zst());
856 !is_1zst
857 })
858}
859
860fn ty_is_known_nonnull<'tcx>(
862 tcx: TyCtxt<'tcx>,
863 typing_env: ty::TypingEnv<'tcx>,
864 ty: Ty<'tcx>,
865 mode: CItemKind,
866) -> bool {
867 let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
868
869 match ty.kind() {
870 ty::FnPtr(..) => true,
871 ty::Ref(..) => true,
872 ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true,
873 ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => {
874 let marked_non_null = nonnull_optimization_guaranteed(tcx, *def);
875
876 if marked_non_null {
877 return true;
878 }
879
880 if def.is_unsafe_cell() || def.is_unsafe_pinned() {
882 return false;
883 }
884
885 def.variants()
886 .iter()
887 .filter_map(|variant| transparent_newtype_field(tcx, variant))
888 .any(|field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args), mode))
889 }
890 ty::Pat(base, pat) => {
891 ty_is_known_nonnull(tcx, typing_env, *base, mode)
892 || pat_ty_is_known_nonnull(tcx, typing_env, *pat)
893 }
894 _ => false,
895 }
896}
897
898fn pat_ty_is_known_nonnull<'tcx>(
899 tcx: TyCtxt<'tcx>,
900 typing_env: ty::TypingEnv<'tcx>,
901 pat: ty::Pattern<'tcx>,
902) -> bool {
903 Option::unwrap_or_default(
904 try {
905 match *pat {
906 ty::PatternKind::Range { start, end } => {
907 let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?;
908 let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?;
909
910 start > 0 && end >= start
913 }
914 ty::PatternKind::Or(patterns) => {
915 patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat))
916 }
917 }
918 },
919 )
920}
921
922fn get_nullable_type<'tcx>(
925 tcx: TyCtxt<'tcx>,
926 typing_env: ty::TypingEnv<'tcx>,
927 ty: Ty<'tcx>,
928) -> Option<Ty<'tcx>> {
929 let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
930
931 Some(match *ty.kind() {
932 ty::Adt(field_def, field_args) => {
933 let inner_field_ty = {
934 let mut first_non_zst_ty =
935 field_def.variants().iter().filter_map(|v| transparent_newtype_field(tcx, v));
936 debug_assert_eq!(
937 first_non_zst_ty.clone().count(),
938 1,
939 "Wrong number of fields for transparent type"
940 );
941 first_non_zst_ty
942 .next_back()
943 .expect("No non-zst fields in transparent type.")
944 .ty(tcx, field_args)
945 };
946 return get_nullable_type(tcx, typing_env, inner_field_ty);
947 }
948 ty::Pat(base, ..) => return get_nullable_type(tcx, typing_env, base),
949 ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => ty,
950 ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl),
953 ty::FnPtr(..) => ty,
956 ref unhandled => {
959 debug!(
960 "get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}",
961 unhandled, ty
962 );
963 return None;
964 }
965 })
966}
967
968fn is_niche_optimization_candidate<'tcx>(
973 tcx: TyCtxt<'tcx>,
974 typing_env: ty::TypingEnv<'tcx>,
975 ty: Ty<'tcx>,
976) -> bool {
977 if tcx.layout_of(typing_env.as_query_input(ty)).is_ok_and(|layout| !layout.is_1zst()) {
978 return false;
979 }
980
981 match ty.kind() {
982 ty::Adt(ty_def, _) => {
983 let non_exhaustive = ty_def.is_variant_list_non_exhaustive();
984 let empty = (ty_def.is_struct() && ty_def.all_fields().next().is_none())
985 || (ty_def.is_enum() && ty_def.variants().is_empty());
986
987 !non_exhaustive && empty
988 }
989 ty::Tuple(tys) => tys.is_empty(),
990 _ => false,
991 }
992}
993
994pub(crate) fn repr_nullable_ptr<'tcx>(
999 tcx: TyCtxt<'tcx>,
1000 typing_env: ty::TypingEnv<'tcx>,
1001 ty: Ty<'tcx>,
1002 ckind: CItemKind,
1003) -> Option<Ty<'tcx>> {
1004 debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty);
1005 match ty.kind() {
1006 ty::Adt(ty_def, args) => {
1007 let field_ty = match &ty_def.variants().raw[..] {
1008 [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
1009 ([], [field]) | ([field], []) => field.ty(tcx, args),
1010 ([field1], [field2]) => {
1011 let ty1 = field1.ty(tcx, args);
1012 let ty2 = field2.ty(tcx, args);
1013
1014 if is_niche_optimization_candidate(tcx, typing_env, ty1) {
1015 ty2
1016 } else if is_niche_optimization_candidate(tcx, typing_env, ty2) {
1017 ty1
1018 } else {
1019 return None;
1020 }
1021 }
1022 _ => return None,
1023 },
1024 _ => return None,
1025 };
1026
1027 if !ty_is_known_nonnull(tcx, typing_env, field_ty, ckind) {
1028 return None;
1029 }
1030
1031 let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, typing_env).ok();
1035 if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) {
1036 bug!("improper_ctypes: Option nonnull optimization not applied?");
1037 }
1038
1039 let field_ty_layout = tcx.layout_of(typing_env.as_query_input(field_ty));
1041 if field_ty_layout.is_err() && !field_ty.has_non_region_param() {
1042 bug!("should be able to compute the layout of non-polymorphic type");
1043 }
1044
1045 let field_ty_abi = &field_ty_layout.ok()?.backend_repr;
1046 if let BackendRepr::Scalar(field_ty_scalar) = field_ty_abi {
1047 match field_ty_scalar.valid_range(&tcx) {
1048 WrappingRange { start: 0, end }
1049 if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 =>
1050 {
1051 return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap());
1052 }
1053 WrappingRange { start: 1, .. } => {
1054 return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap());
1055 }
1056 WrappingRange { start, end } => {
1057 unreachable!("Unhandled start and end range: ({}, {})", start, end)
1058 }
1059 };
1060 }
1061 None
1062 }
1063 ty::Pat(base, pat) => get_nullable_type_from_pat(tcx, typing_env, *base, *pat),
1064 _ => None,
1065 }
1066}
1067
1068fn get_nullable_type_from_pat<'tcx>(
1069 tcx: TyCtxt<'tcx>,
1070 typing_env: ty::TypingEnv<'tcx>,
1071 base: Ty<'tcx>,
1072 pat: ty::Pattern<'tcx>,
1073) -> Option<Ty<'tcx>> {
1074 match *pat {
1075 ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, base),
1076 ty::PatternKind::Or(patterns) => {
1077 let first = get_nullable_type_from_pat(tcx, typing_env, base, patterns[0])?;
1078 for &pat in &patterns[1..] {
1079 assert_eq!(first, get_nullable_type_from_pat(tcx, typing_env, base, pat)?);
1080 }
1081 Some(first)
1082 }
1083 }
1084}
1085
1086impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1087 fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
1089 if let ty::Array(..) = ty.kind() {
1090 self.emit_ffi_unsafe_type_lint(
1091 ty,
1092 sp,
1093 fluent::lint_improper_ctypes_array_reason,
1094 Some(fluent::lint_improper_ctypes_array_help),
1095 );
1096 true
1097 } else {
1098 false
1099 }
1100 }
1101
1102 fn check_field_type_for_ffi(
1104 &self,
1105 acc: &mut CTypesVisitorState<'tcx>,
1106 field: &ty::FieldDef,
1107 args: GenericArgsRef<'tcx>,
1108 ) -> FfiResult<'tcx> {
1109 let field_ty = field.ty(self.cx.tcx, args);
1110 let field_ty = self
1111 .cx
1112 .tcx
1113 .try_normalize_erasing_regions(self.cx.typing_env(), field_ty)
1114 .unwrap_or(field_ty);
1115 self.check_type_for_ffi(acc, field_ty)
1116 }
1117
1118 fn check_variant_for_ffi(
1120 &self,
1121 acc: &mut CTypesVisitorState<'tcx>,
1122 ty: Ty<'tcx>,
1123 def: ty::AdtDef<'tcx>,
1124 variant: &ty::VariantDef,
1125 args: GenericArgsRef<'tcx>,
1126 ) -> FfiResult<'tcx> {
1127 use FfiResult::*;
1128 let transparent_with_all_zst_fields = if def.repr().transparent() {
1129 if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
1130 match self.check_field_type_for_ffi(acc, field, args) {
1132 FfiUnsafe { ty, .. } if ty.is_unit() => (),
1133 r => return r,
1134 }
1135
1136 false
1137 } else {
1138 true
1141 }
1142 } else {
1143 false
1144 };
1145
1146 let mut all_phantom = !variant.fields.is_empty();
1148 for field in &variant.fields {
1149 all_phantom &= match self.check_field_type_for_ffi(acc, field, args) {
1150 FfiSafe => false,
1151 FfiUnsafe { ty, .. } if ty.is_unit() => false,
1153 FfiPhantom(..) => true,
1154 r @ FfiUnsafe { .. } => return r,
1155 }
1156 }
1157
1158 if all_phantom {
1159 FfiPhantom(ty)
1160 } else if transparent_with_all_zst_fields {
1161 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
1162 } else {
1163 FfiSafe
1164 }
1165 }
1166
1167 fn check_type_for_ffi(
1170 &self,
1171 acc: &mut CTypesVisitorState<'tcx>,
1172 ty: Ty<'tcx>,
1173 ) -> FfiResult<'tcx> {
1174 use FfiResult::*;
1175
1176 let tcx = self.cx.tcx;
1177
1178 if !acc.cache.insert(ty) {
1183 return FfiSafe;
1184 }
1185
1186 match *ty.kind() {
1187 ty::Adt(def, args) => {
1188 if let Some(boxed) = ty.boxed_ty()
1189 && matches!(self.mode, CItemKind::Definition)
1190 {
1191 if boxed.is_sized(tcx, self.cx.typing_env()) {
1192 return FfiSafe;
1193 } else {
1194 return FfiUnsafe {
1195 ty,
1196 reason: fluent::lint_improper_ctypes_box,
1197 help: None,
1198 };
1199 }
1200 }
1201 if def.is_phantom_data() {
1202 return FfiPhantom(ty);
1203 }
1204 match def.adt_kind() {
1205 AdtKind::Struct | AdtKind::Union => {
1206 if let Some(sym::cstring_type | sym::cstr_type) =
1207 tcx.get_diagnostic_name(def.did())
1208 && !acc.base_ty.is_mutable_ptr()
1209 {
1210 return FfiUnsafe {
1211 ty,
1212 reason: fluent::lint_improper_ctypes_cstr_reason,
1213 help: Some(fluent::lint_improper_ctypes_cstr_help),
1214 };
1215 }
1216
1217 if !def.repr().c() && !def.repr().transparent() {
1218 return FfiUnsafe {
1219 ty,
1220 reason: if def.is_struct() {
1221 fluent::lint_improper_ctypes_struct_layout_reason
1222 } else {
1223 fluent::lint_improper_ctypes_union_layout_reason
1224 },
1225 help: if def.is_struct() {
1226 Some(fluent::lint_improper_ctypes_struct_layout_help)
1227 } else {
1228 Some(fluent::lint_improper_ctypes_union_layout_help)
1229 },
1230 };
1231 }
1232
1233 if def.non_enum_variant().field_list_has_applicable_non_exhaustive() {
1234 return FfiUnsafe {
1235 ty,
1236 reason: if def.is_struct() {
1237 fluent::lint_improper_ctypes_struct_non_exhaustive
1238 } else {
1239 fluent::lint_improper_ctypes_union_non_exhaustive
1240 },
1241 help: None,
1242 };
1243 }
1244
1245 if def.non_enum_variant().fields.is_empty() {
1246 return FfiUnsafe {
1247 ty,
1248 reason: if def.is_struct() {
1249 fluent::lint_improper_ctypes_struct_fieldless_reason
1250 } else {
1251 fluent::lint_improper_ctypes_union_fieldless_reason
1252 },
1253 help: if def.is_struct() {
1254 Some(fluent::lint_improper_ctypes_struct_fieldless_help)
1255 } else {
1256 Some(fluent::lint_improper_ctypes_union_fieldless_help)
1257 },
1258 };
1259 }
1260
1261 self.check_variant_for_ffi(acc, ty, def, def.non_enum_variant(), args)
1262 }
1263 AdtKind::Enum => {
1264 if def.variants().is_empty() {
1265 return FfiSafe;
1267 }
1268 if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none()
1271 {
1272 if let Some(ty) =
1274 repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty, self.mode)
1275 {
1276 return self.check_type_for_ffi(acc, ty);
1277 }
1278
1279 return FfiUnsafe {
1280 ty,
1281 reason: fluent::lint_improper_ctypes_enum_repr_reason,
1282 help: Some(fluent::lint_improper_ctypes_enum_repr_help),
1283 };
1284 }
1285
1286 use improper_ctypes::check_non_exhaustive_variant;
1287
1288 let non_exhaustive = def.variant_list_has_applicable_non_exhaustive();
1289 let ret = def.variants().iter().try_for_each(|variant| {
1291 check_non_exhaustive_variant(non_exhaustive, variant)
1292 .map_break(|reason| FfiUnsafe { ty, reason, help: None })?;
1293
1294 match self.check_variant_for_ffi(acc, ty, def, variant, args) {
1295 FfiSafe => ControlFlow::Continue(()),
1296 r => ControlFlow::Break(r),
1297 }
1298 });
1299 if let ControlFlow::Break(result) = ret {
1300 return result;
1301 }
1302
1303 FfiSafe
1304 }
1305 }
1306 }
1307
1308 ty::Char => FfiUnsafe {
1309 ty,
1310 reason: fluent::lint_improper_ctypes_char_reason,
1311 help: Some(fluent::lint_improper_ctypes_char_help),
1312 },
1313
1314 ty::Pat(base, ..) => self.check_type_for_ffi(acc, base),
1317
1318 ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe,
1320
1321 ty::Slice(_) => FfiUnsafe {
1322 ty,
1323 reason: fluent::lint_improper_ctypes_slice_reason,
1324 help: Some(fluent::lint_improper_ctypes_slice_help),
1325 },
1326
1327 ty::Dynamic(..) => {
1328 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None }
1329 }
1330
1331 ty::Str => FfiUnsafe {
1332 ty,
1333 reason: fluent::lint_improper_ctypes_str_reason,
1334 help: Some(fluent::lint_improper_ctypes_str_help),
1335 },
1336
1337 ty::Tuple(..) => FfiUnsafe {
1338 ty,
1339 reason: fluent::lint_improper_ctypes_tuple_reason,
1340 help: Some(fluent::lint_improper_ctypes_tuple_help),
1341 },
1342
1343 ty::RawPtr(ty, _) | ty::Ref(_, ty, _)
1344 if {
1345 matches!(self.mode, CItemKind::Definition)
1346 && ty.is_sized(self.cx.tcx, self.cx.typing_env())
1347 } =>
1348 {
1349 FfiSafe
1350 }
1351
1352 ty::RawPtr(ty, _)
1353 if match ty.kind() {
1354 ty::Tuple(tuple) => tuple.is_empty(),
1355 _ => false,
1356 } =>
1357 {
1358 FfiSafe
1359 }
1360
1361 ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(acc, ty),
1362
1363 ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty),
1364
1365 ty::FnPtr(sig_tys, hdr) => {
1366 let sig = sig_tys.with(hdr);
1367 if sig.abi().is_rustic_abi() {
1368 return FfiUnsafe {
1369 ty,
1370 reason: fluent::lint_improper_ctypes_fnptr_reason,
1371 help: Some(fluent::lint_improper_ctypes_fnptr_help),
1372 };
1373 }
1374
1375 let sig = tcx.instantiate_bound_regions_with_erased(sig);
1376 for arg in sig.inputs() {
1377 match self.check_type_for_ffi(acc, *arg) {
1378 FfiSafe => {}
1379 r => return r,
1380 }
1381 }
1382
1383 let ret_ty = sig.output();
1384 if ret_ty.is_unit() {
1385 return FfiSafe;
1386 }
1387
1388 self.check_type_for_ffi(acc, ret_ty)
1389 }
1390
1391 ty::Foreign(..) => FfiSafe,
1392
1393 ty::Alias(ty::Opaque, ..) => {
1396 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None }
1397 }
1398
1399 ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
1402 if matches!(self.mode, CItemKind::Definition) =>
1403 {
1404 FfiSafe
1405 }
1406
1407 ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
1408
1409 ty::Param(..)
1410 | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..)
1411 | ty::Infer(..)
1412 | ty::Bound(..)
1413 | ty::Error(_)
1414 | ty::Closure(..)
1415 | ty::CoroutineClosure(..)
1416 | ty::Coroutine(..)
1417 | ty::CoroutineWitness(..)
1418 | ty::Placeholder(..)
1419 | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
1420 }
1421 }
1422
1423 fn emit_ffi_unsafe_type_lint(
1424 &mut self,
1425 ty: Ty<'tcx>,
1426 sp: Span,
1427 note: DiagMessage,
1428 help: Option<DiagMessage>,
1429 ) {
1430 let lint = match self.mode {
1431 CItemKind::Declaration => IMPROPER_CTYPES,
1432 CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
1433 };
1434 let desc = match self.mode {
1435 CItemKind::Declaration => "block",
1436 CItemKind::Definition => "fn",
1437 };
1438 let span_note = if let ty::Adt(def, _) = ty.kind()
1439 && let Some(sp) = self.cx.tcx.hir_span_if_local(def.did())
1440 {
1441 Some(sp)
1442 } else {
1443 None
1444 };
1445 self.cx.emit_span_lint(
1446 lint,
1447 sp,
1448 ImproperCTypes { ty, desc, label: sp, help, note, span_note },
1449 );
1450 }
1451
1452 fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
1453 struct ProhibitOpaqueTypes;
1454 impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes {
1455 type Result = ControlFlow<Ty<'tcx>>;
1456
1457 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
1458 if !ty.has_opaque_types() {
1459 return ControlFlow::Continue(());
1460 }
1461
1462 if let ty::Alias(ty::Opaque, ..) = ty.kind() {
1463 ControlFlow::Break(ty)
1464 } else {
1465 ty.super_visit_with(self)
1466 }
1467 }
1468 }
1469
1470 if let Some(ty) = self
1471 .cx
1472 .tcx
1473 .try_normalize_erasing_regions(self.cx.typing_env(), ty)
1474 .unwrap_or(ty)
1475 .visit_with(&mut ProhibitOpaqueTypes)
1476 .break_value()
1477 {
1478 self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None);
1479 true
1480 } else {
1481 false
1482 }
1483 }
1484
1485 fn check_type_for_ffi_and_report_errors(
1486 &mut self,
1487 sp: Span,
1488 ty: Ty<'tcx>,
1489 is_static: bool,
1490 is_return_type: bool,
1491 ) {
1492 if self.check_for_opaque_ty(sp, ty) {
1493 return;
1495 }
1496
1497 let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty);
1498
1499 if !is_static && self.check_for_array_ty(sp, ty) {
1503 return;
1504 }
1505
1506 if is_return_type && ty.is_unit() {
1510 return;
1511 }
1512
1513 let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty };
1514 match self.check_type_for_ffi(&mut acc, ty) {
1515 FfiResult::FfiSafe => {}
1516 FfiResult::FfiPhantom(ty) => {
1517 self.emit_ffi_unsafe_type_lint(
1518 ty,
1519 sp,
1520 fluent::lint_improper_ctypes_only_phantomdata,
1521 None,
1522 );
1523 }
1524 FfiResult::FfiUnsafe { ty, reason, help } => {
1525 self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
1526 }
1527 }
1528 }
1529
1530 fn check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
1535 let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity();
1536 let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig);
1537
1538 for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1539 for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) {
1540 self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, false);
1541 }
1542 }
1543
1544 if let hir::FnRetTy::Return(ret_hir) = decl.output {
1545 for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(ret_hir, sig.output()) {
1546 self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, true);
1547 }
1548 }
1549 }
1550
1551 fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
1553 let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity();
1554 let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig);
1555
1556 for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1557 self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false);
1558 }
1559
1560 if let hir::FnRetTy::Return(ret_hir) = decl.output {
1561 self.check_type_for_ffi_and_report_errors(ret_hir.span, sig.output(), false, true);
1562 }
1563 }
1564
1565 fn check_foreign_static(&mut self, id: hir::OwnerId, span: Span) {
1566 let ty = self.cx.tcx.type_of(id).instantiate_identity();
1567 self.check_type_for_ffi_and_report_errors(span, ty, true, false);
1568 }
1569
1570 fn find_fn_ptr_ty_with_external_abi(
1574 &self,
1575 hir_ty: &hir::Ty<'tcx>,
1576 ty: Ty<'tcx>,
1577 ) -> Vec<(Ty<'tcx>, Span)> {
1578 struct FnPtrFinder<'tcx> {
1579 spans: Vec<Span>,
1580 tys: Vec<Ty<'tcx>>,
1581 }
1582
1583 impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> {
1584 fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) {
1585 debug!(?ty);
1586 if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind
1587 && !abi.is_rustic_abi()
1588 {
1589 self.spans.push(ty.span);
1590 }
1591
1592 hir::intravisit::walk_ty(self, ty)
1593 }
1594 }
1595
1596 impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'tcx> {
1597 type Result = ();
1598
1599 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
1600 if let ty::FnPtr(_, hdr) = ty.kind()
1601 && !hdr.abi.is_rustic_abi()
1602 {
1603 self.tys.push(ty);
1604 }
1605
1606 ty.super_visit_with(self)
1607 }
1608 }
1609
1610 let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() };
1611 ty.visit_with(&mut visitor);
1612 visitor.visit_ty_unambig(hir_ty);
1613
1614 iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect()
1615 }
1616}
1617
1618impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations {
1619 fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
1620 let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration };
1621 let abi = cx.tcx.hir_get_foreign_abi(it.hir_id());
1622
1623 match it.kind {
1624 hir::ForeignItemKind::Fn(sig, _, _) => {
1625 if abi.is_rustic_abi() {
1626 vis.check_fn(it.owner_id.def_id, sig.decl)
1627 } else {
1628 vis.check_foreign_fn(it.owner_id.def_id, sig.decl);
1629 }
1630 }
1631 hir::ForeignItemKind::Static(ty, _, _) if !abi.is_rustic_abi() => {
1632 vis.check_foreign_static(it.owner_id, ty.span);
1633 }
1634 hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
1635 }
1636 }
1637}
1638
1639impl ImproperCTypesDefinitions {
1640 fn check_ty_maybe_containing_foreign_fnptr<'tcx>(
1641 &mut self,
1642 cx: &LateContext<'tcx>,
1643 hir_ty: &'tcx hir::Ty<'_>,
1644 ty: Ty<'tcx>,
1645 ) {
1646 let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
1647 for (fn_ptr_ty, span) in vis.find_fn_ptr_ty_with_external_abi(hir_ty, ty) {
1648 vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false);
1649 }
1650 }
1651
1652 fn check_arg_for_power_alignment<'tcx>(
1653 &mut self,
1654 cx: &LateContext<'tcx>,
1655 ty: Ty<'tcx>,
1656 ) -> bool {
1657 assert!(cx.tcx.sess.target.os == "aix");
1658 if ty.is_floating_point() && ty.primitive_size(cx.tcx).bytes() > 4 {
1665 return true;
1666 } else if let Adt(adt_def, _) = ty.kind()
1667 && adt_def.is_struct()
1668 && adt_def.repr().c()
1669 && !adt_def.repr().packed()
1670 && adt_def.repr().align.is_none()
1671 {
1672 let struct_variant = adt_def.variant(VariantIdx::ZERO);
1673 for struct_field in &struct_variant.fields {
1677 let field_ty = cx.tcx.type_of(struct_field.did).instantiate_identity();
1678 if self.check_arg_for_power_alignment(cx, field_ty) {
1679 return true;
1680 }
1681 }
1682 }
1683 return false;
1684 }
1685
1686 fn check_struct_for_power_alignment<'tcx>(
1687 &mut self,
1688 cx: &LateContext<'tcx>,
1689 item: &'tcx hir::Item<'tcx>,
1690 ) {
1691 let adt_def = cx.tcx.adt_def(item.owner_id.to_def_id());
1692 if adt_def.repr().c()
1695 && !adt_def.repr().packed()
1696 && adt_def.repr().align.is_none()
1697 && cx.tcx.sess.target.os == "aix"
1698 && !adt_def.all_fields().next().is_none()
1699 {
1700 let struct_variant_data = item.expect_struct().2;
1701 for field_def in struct_variant_data.fields().iter().skip(1) {
1702 let def_id = field_def.def_id;
1706 let ty = cx.tcx.type_of(def_id).instantiate_identity();
1707 if self.check_arg_for_power_alignment(cx, ty) {
1708 cx.emit_span_lint(USES_POWER_ALIGNMENT, field_def.span, UsesPowerAlignment);
1709 }
1710 }
1711 }
1712 }
1713}
1714
1715impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
1723 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
1724 match item.kind {
1725 hir::ItemKind::Static(_, _, ty, _)
1726 | hir::ItemKind::Const(_, _, ty, _)
1727 | hir::ItemKind::TyAlias(_, _, ty) => {
1728 self.check_ty_maybe_containing_foreign_fnptr(
1729 cx,
1730 ty,
1731 cx.tcx.type_of(item.owner_id).instantiate_identity(),
1732 );
1733 }
1734 hir::ItemKind::Fn { .. } => {}
1736 hir::ItemKind::Struct(..) => {
1739 self.check_struct_for_power_alignment(cx, item);
1740 }
1741 hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {}
1743 hir::ItemKind::Impl(..)
1745 | hir::ItemKind::TraitAlias(..)
1746 | hir::ItemKind::Trait(..)
1747 | hir::ItemKind::GlobalAsm { .. }
1748 | hir::ItemKind::ForeignMod { .. }
1749 | hir::ItemKind::Mod(..)
1750 | hir::ItemKind::Macro(..)
1751 | hir::ItemKind::Use(..)
1752 | hir::ItemKind::ExternCrate(..) => {}
1753 }
1754 }
1755
1756 fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
1757 self.check_ty_maybe_containing_foreign_fnptr(
1758 cx,
1759 field.ty,
1760 cx.tcx.type_of(field.def_id).instantiate_identity(),
1761 );
1762 }
1763
1764 fn check_fn(
1765 &mut self,
1766 cx: &LateContext<'tcx>,
1767 kind: hir::intravisit::FnKind<'tcx>,
1768 decl: &'tcx hir::FnDecl<'_>,
1769 _: &'tcx hir::Body<'_>,
1770 _: Span,
1771 id: LocalDefId,
1772 ) {
1773 use hir::intravisit::FnKind;
1774
1775 let abi = match kind {
1776 FnKind::ItemFn(_, _, header, ..) => header.abi,
1777 FnKind::Method(_, sig, ..) => sig.header.abi,
1778 _ => return,
1779 };
1780
1781 let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
1782 if abi.is_rustic_abi() {
1783 vis.check_fn(id, decl);
1784 } else {
1785 vis.check_foreign_fn(id, decl);
1786 }
1787 }
1788}
1789
1790declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]);
1791
1792impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
1793 fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
1794 if let hir::ItemKind::Enum(_, _, ref enum_definition) = it.kind {
1795 let t = cx.tcx.type_of(it.owner_id).instantiate_identity();
1796 let ty = cx.tcx.erase_regions(t);
1797 let Ok(layout) = cx.layout_of(ty) else { return };
1798 let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, variants, .. } =
1799 &layout.variants
1800 else {
1801 return;
1802 };
1803
1804 let tag_size = tag.size(&cx.tcx).bytes();
1805
1806 debug!(
1807 "enum `{}` is {} bytes large with layout:\n{:#?}",
1808 t,
1809 layout.size.bytes(),
1810 layout
1811 );
1812
1813 let (largest, slargest, largest_index) = iter::zip(enum_definition.variants, variants)
1814 .map(|(variant, variant_layout)| {
1815 let bytes = variant_layout.size.bytes().saturating_sub(tag_size);
1817
1818 debug!("- variant `{}` is {} bytes large", variant.ident, bytes);
1819 bytes
1820 })
1821 .enumerate()
1822 .fold((0, 0, 0), |(l, s, li), (idx, size)| {
1823 if size > l {
1824 (size, l, idx)
1825 } else if size > s {
1826 (l, size, li)
1827 } else {
1828 (l, s, li)
1829 }
1830 });
1831
1832 if largest > slargest * 3 && slargest > 0 {
1835 cx.emit_span_lint(
1836 VARIANT_SIZE_DIFFERENCES,
1837 enum_definition.variants[largest_index].span,
1838 VariantSizeDifferencesDiag { largest },
1839 );
1840 }
1841 }
1842 }
1843}
1844
1845declare_lint! {
1846 INVALID_ATOMIC_ORDERING,
1883 Deny,
1884 "usage of invalid atomic ordering in atomic operations and memory fences"
1885}
1886
1887declare_lint_pass!(InvalidAtomicOrdering => [INVALID_ATOMIC_ORDERING]);
1888
1889impl InvalidAtomicOrdering {
1890 fn inherent_atomic_method_call<'hir>(
1891 cx: &LateContext<'_>,
1892 expr: &Expr<'hir>,
1893 recognized_names: &[Symbol], ) -> Option<(Symbol, &'hir [Expr<'hir>])> {
1895 const ATOMIC_TYPES: &[Symbol] = &[
1896 sym::AtomicBool,
1897 sym::AtomicPtr,
1898 sym::AtomicUsize,
1899 sym::AtomicU8,
1900 sym::AtomicU16,
1901 sym::AtomicU32,
1902 sym::AtomicU64,
1903 sym::AtomicU128,
1904 sym::AtomicIsize,
1905 sym::AtomicI8,
1906 sym::AtomicI16,
1907 sym::AtomicI32,
1908 sym::AtomicI64,
1909 sym::AtomicI128,
1910 ];
1911 if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind
1912 && recognized_names.contains(&method_path.ident.name)
1913 && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
1914 && let Some(impl_did) = cx.tcx.impl_of_method(m_def_id)
1915 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
1916 && cx.tcx.trait_id_of_impl(impl_did).is_none()
1918 && let parent = cx.tcx.parent(adt.did())
1919 && cx.tcx.is_diagnostic_item(sym::atomic_mod, parent)
1920 && ATOMIC_TYPES.contains(&cx.tcx.item_name(adt.did()))
1921 {
1922 return Some((method_path.ident.name, args));
1923 }
1924 None
1925 }
1926
1927 fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol> {
1928 let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None };
1929 let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?;
1930 let tcx = cx.tcx;
1931 let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering);
1932 let name = tcx.item_name(did);
1933 let parent = tcx.parent(did);
1934 [sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find(
1935 |&ordering| {
1936 name == ordering
1937 && (Some(parent) == atomic_ordering
1938 || tcx.opt_parent(parent) == atomic_ordering)
1940 },
1941 )
1942 }
1943
1944 fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
1945 if let Some((method, args)) =
1946 Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
1947 && let Some((ordering_arg, invalid_ordering)) = match method {
1948 sym::load => Some((&args[0], sym::Release)),
1949 sym::store => Some((&args[1], sym::Acquire)),
1950 _ => None,
1951 }
1952 && let Some(ordering) = Self::match_ordering(cx, ordering_arg)
1953 && (ordering == invalid_ordering || ordering == sym::AcqRel)
1954 {
1955 if method == sym::load {
1956 cx.emit_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad);
1957 } else {
1958 cx.emit_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore);
1959 };
1960 }
1961 }
1962
1963 fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
1964 if let ExprKind::Call(func, args) = expr.kind
1965 && let ExprKind::Path(ref func_qpath) = func.kind
1966 && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
1967 && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
1968 && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
1969 {
1970 cx.emit_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence);
1971 }
1972 }
1973
1974 fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
1975 let Some((method, args)) = Self::inherent_atomic_method_call(
1976 cx,
1977 expr,
1978 &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak],
1979 ) else {
1980 return;
1981 };
1982
1983 let fail_order_arg = match method {
1984 sym::fetch_update => &args[1],
1985 sym::compare_exchange | sym::compare_exchange_weak => &args[3],
1986 _ => return,
1987 };
1988
1989 let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
1990
1991 if matches!(fail_ordering, sym::Release | sym::AcqRel) {
1992 cx.emit_span_lint(
1993 INVALID_ATOMIC_ORDERING,
1994 fail_order_arg.span,
1995 InvalidAtomicOrderingDiag { method, fail_order_arg_span: fail_order_arg.span },
1996 );
1997 }
1998 }
1999}
2000
2001impl<'tcx> LateLintPass<'tcx> for InvalidAtomicOrdering {
2002 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
2003 Self::check_atomic_load_store(cx, expr);
2004 Self::check_memory_fence(cx, expr);
2005 Self::check_atomic_compare_exchange(cx, expr);
2006 }
2007}