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